您所在的位置:首页 / 知识分享

我的前端自学笔记 => 模块化开发与规范化标准

2020.08.24

811

老余

从今天开始,我要总结一下自己学习前端的笔记 本人前端小白一枚,只是整理一下自己的学习过程,第一次在掘金发表自己的文章,有问题欢迎大家及时指出

一、模块化概述

模块化是一种思想。模块化是主流的代码组织方式,通过把复杂代码按照功能的不同划分为不同的模块,单独进行维护的方式提高开发效率,降低维护成本。

二、模块化的演变过程

文件划分方式

将每个功能及状态数据单独存放到不同的文件中,约定每个文件都是独立的模块,使用的时候就是将模块引入到页面中,一个script标签对应一个模块,然后再代码中直接调用全局成员(变量或函数)

缺点:

污染全局作用域 命名冲突

命名空间方式

每个模块只暴露一个全局对象,所有的模块成员都挂载到对象下

缺点:

无私有空间 模块内部成员可以被访问修改 模块之间存在依赖关系

IIFE(立即执行函数)

使用立即执行函数包裹代码,要输出的遍历挂载到一个全局对象上 变量拥有了私有空间,只有通过闭包修改和访问变量 参数作为依赖声明去使用,使得每个模块的依赖关系变得明显

三、模块化规范

CommentJs规范

NodeJs提出的一种标准,以同步模式加载模块

一个文件就是一个模块 每个模块都有单独的作用域 使用module.export导出成员 通过require函数载入模块

AMD(Asynchronous Module Definition)异步模块定义规范

Require.js

Sea.js + CMD(Common Module Definition)通用模块规范

ES Module

// 通过给script添加type=module属性,就可以使用ES Module的标准执行其中的JS代码 <script type="module"> console.log(123)
</script> 复制代码
ES Module特点:
自动采用严格模式,忽略’use strict’
每个ESM模块都是单独的私有作用域
ESM是通过CORS去请求外部JS模块的
ESM的script标签会延迟脚本执行 复制代码

导出

// 导出变量 let bar = 'hello' export { bar } // 导出类 export class Test{} // 导出方法 export function fn(){} // 导出默认成员 let num1 = 1 let num2 = 2 // default后是一个对象 export default {num1,num2} 复制代码
注意!:
export { } 是一个固定用法,并非导出了一个对象
导出的值是引用关系,是只读成员,不可进行修改
导入的时候必须使用完整的名称,不可省略扩展名 复制代码

导入

// 基本用法,路径必须完整 import {foo} from './foo.js' // 导入默认对象,通过as可修改别名 import foo as fooName from './foo.js' // 动态导入,返回的是promise对象 import('./module.js').then(function(module){ console.log(module)
}) 复制代码
注意!:
导入时,可以使用绝对路径,相对路径如果是同目录必须使用‘./’,不可省略
只加载不提取,可以使用:import module.js或者 import {} from module.js
全部导入,使用:import * as mod from module.js
import 关键词只能出现在最外层的作用域 复制代码

ES Module 与 commonJS的交互

ESModules中可以导入CommentJs模块
commentJs不可以导入ESModule模块
CommentJs始终只导出一个默认成员 复制代码
在commonJS中存在内置对象如module、require、__dirname、__filename等,ES Module中不存在这种内置
对象,__dirname、__filename可以通过import.meta.url 、 import.meta.path获取到commonJS中内置
对象的相同路径和目录 复制代码

新版本的node.js可以通过在package.json中修改type:module来使用ES Module规范

四、模块化打包

ES Module存在环境兼容问题,通过模块化方式划分的模块较多,网络请求频繁,在前端应用开发中不仅仅需要JavaScript代码需要模块化,随着应用的日益复杂,html,css同样也面临相同的问题,也就是说,所有的前端资源都需要模块化。

所需要的工具需要满足的条件:

新特性代码编译 模块化JavaScript打包 支持不同文件类型的资源模块

打包工具:解决前端整体的模块化,并不单指JavaScript模块化

1.webpack

webpack是目前最主流的前端模块化打包工具之一,在4版本之后支持无配置打包,打包路径从src/index.js为入口开始,将打包文件输出到dist/main.js中,可以通过配置文件对其进行配置

webpack的配置文件入口为webpack.config.js,这个文件是运行在node环境下的文件,所以其中的配置需要以commonJS规范来编写

const path = require('path') module.exports = { mode: 'none', // 构建模式,不设置会默认采用production默认构建 production:生产 development:开发 entry: './src/main.js', // 打包文件入口,绝对路径 output: { filename: 'bundle.js', // 输出文件名 // path: 'output' // 报错,需要绝对路径 path: path.join(__dirname, 'dist'), // 输出路径,绝对路径 publicPath: 'dist/' // 网站根目录,防止资源加载时无法获取对应dist路径 }
} 复制代码

webpack在构建时,会默认以production的方式去构建,可以通过webpack --mode development/production/none对构建方式进行设置

在官方文档中是这样介绍的:

webpack打包结果的运行原理

这里我先定义了两个js文件

// heading.js export default () => { const element = document.createElement('h2')

  element.textContent = 'Hello world' element.addEventListener('click', () => {
    alert('Hello webpack')
  }) return element
} // main.js import createHeading from './heading.js' const heading = createHeading() document.body.append(heading) 复制代码

通过webpack进行打包生成bundle.js文件,这里为了调试方便,将mode设置为none

首先在第一行,参数是打包进去的两个js文件在生成的bundle.js文件中可以找到这段代码,在这段代码中返回的是webpack的传入的参数中index为0的函数,我们可以找到这个函数在这里首先对这个模块进行判断是否进行了加载,若加载过,则直接从缓存中获取,没有则创建这个函数,并且调用这个模块的函数,在调用时,传入刚创建的模块对象、导出成员对象和require函数,这样可以在模块内部通过module.exports导出成员,并且通过webpack.require导入在这个模块内部,首先调用了.r这个函数,这个函数内部为这个模块添加了esModule的标记,通过这个标记对外表示这个是一个esModule模块随后这个模块内部再次调用了require函数,此时为index为1的模块内容,并重复上述的操作,创建完之后执行index为1的模块内的代码

webpack资源模块加载

webpack默认对js语言进行打包,直接打包css文件时,会报错,所以在对css进行打包时,需要安装css-loader和style-loader

// webpack默认打包js代码,在module中配置rules针对其他资源模块的规则配置 module: { rules: [
            { test: /.css$/, // 正则表达式,匹配打包过程的文件后缀 // 指定匹配文件使用的loader 执行顺序是从后到前的 use: [ 'style-loader', // 将css-loader转换过后的结果通过style标签的方式加载到页面中 'css-loader' // 将css文件转换成js文件,push到数组中 ] 
            }
        ]
    } 复制代码

这里会产生一个问题,当你打包js文件的时候,需要在js文件中引用对应的css文件,才会正常工作,但是在正常开发过程中基本上是将js和css分离开,所以webpack这里的思想是为了确保两点

1.js需要这些文件才能正常工作
2.避免上线过程中代码不缺失 复制代码

webpack文件资源加载器

1.首先安装file-loader,随后在module/rules对其进行配置。注意:需要对publicPath进行配置,否则会产生找不到对应资源路径的问题 2.安装url-loader,在打包过程中会以data:url的形式打包到对应的js文件中,不会生成对应文件

{ test: /.png$/, // 正则表达式,匹配打包过程的文件后缀 use: [ 'file-loader' // 文件加载器,将.png格式的文件打包到dist目录下,会生成文件 // 'url-loader'  // url加载器,将.png格式的文件以data:url的形式打包到bundle.js中,不会生成对应的资源文件 ] 
} 复制代码

通过file-loader打包后的结果:

通过url-loader打包后的结果:可以对比出两种不同的文件打包方式,打包结果是不同的,我们可以通过对打包文件的大小进行判断,对于小文件可以通过url-loader进行打包,减少请求次数,对于大文件建议使用file-loader进行打包单独提取存放,提高加载速度,这里可以在配置文件中对文件大小进行限制
{ test: /.png$/, // 正则表达式,匹配打包过程的文件后缀 use: { loader: 'url-loader', // 以10KB为标准进行校验,超过10KB使用file-loader,不超过用url-loader options: { limit: 10 * 1024 // 10KB }
    }
} 复制代码

需要注意的是,在使用这种配置方式时,需要同时安装file-loader和url-loader,否则打包失败报错

webpack常用加载器
1.编译转换类,转换为JS代码,如css-loader
2.文件操作类,将资源文件拷贝到输出目录,将文件访问路径向外导出,如:file-loader
3.代码检查器,统一代码风格,提高代码质量,如:es-loader 复制代码

webpack 处理ES2015

webpack只是打包工具,不会处理ES6或者其他新特性,所以可以使用加载器来编译转化代码,babel-loader,babel-loader依赖于babel的核心模块,@babel/core和@babel/preset-env

{ test: /.js$/, // 正则表达式,匹配打包过程的文件后缀 use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env']
        }
    }
}, 复制代码

webpack模块加载方式

遵循CommonJS标准的require函数。对于ES的默认导出,要通过require(’./XXX’).default的形式获取,遵循AMD标准的define函数和require函数

css-loader在处理css代码时,遇到了background属性中的url函数,发现是引入的资源文件是png格式的文件,则将这个资源文件 交给url-loader
处理

Loader加载的非JavaScript也会触发资源加载 复制代码

对于html文件,也会引入资源文件,例如img的src和a的href,我们可以在webpack.config.js中对其进行配置,默认打包过程中,html文件只识别img:src属性,需要在options中配置需要识别的属性

// fooer.html <footer>
  <!-- <img src="better.png" alt="better" width="256"> --> <a href="better.png">download png</a> </footer> // main.js import footerHtml from './footer.html' document.write(footerHtml) // webpack.config.js { test: /.html$/, // 正则表达式,匹配打包过程的文件后缀 use: { loader: 'html-loader', options: { // html文件在打包过程中默认只识别img:src属性,这里需要额外添加属性 attributes: { list: [
                  { tag: 'img', attribute: 'src', type: 'src',
                  },
                  { tag: 'a', attribute: 'href', type: 'href',
                  },
                ]
            }
        }
    }
} 复制代码

webpack核心工作原理

首先webpack会找到一个入口文件,一般入口文件为js文件,通过这个文件中的import和require语句,解析推断出这个文件所依赖的模块,以这种方式向下寻找所有有关联的模块,形成一种依赖关系的树,最后通过递归的方式找到其中的依赖文件,通过webpack.config.js中的rules属性,找到对应的loader,合并到bundle.js中

loader是webpack的核心 复制代码

webpack loader的核心原理

loader的核心原理是管道,通过管道的方式体现出输出输入的过程

// markdown-loader.js const marked = require('marked') module.exports = source => { // console.log(source) // webpack返回的内容必须是js代码或者其他,直接返回字符串,webpack打包会报错,提示需要一个其他的loader处理返回的内容 // return 'hello~' const html = marked(source) // 自己通过拼接的方式返回js代码 return `module.exports = "${JSON.stringify(html)}"` return `export default "${JSON.stringify(html)}"` // 返回html字符串交给下一个loader处理 return html
} // webpack.config.js const path = require('path') module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'bundle.js', path: path.join(__dirname, 'dist'), publicPath: 'dist/' }, module: { rules: [
      { test: /.md$/,
        use: [ 'html-loader', './markdown-loader' ]
      }
    ]
  }
} 复制代码

webpack插件

插件机制是Webpack当中一个核心特性,目的是增强webpack在项目自动化方面的能力。loader就是负责实现我们项目当中各种各样资源模块加载。从而去实现整体项目的打包。plugin则是用来去解决项目中除了资源加载以外其他的一些自动化工作。例如,可以帮我们去实现,自动在打包之前去清除dist的目录。

clean-webpack-plugin

首先安装clean-webpack-plugin插件,然后通过require的方式引用该插件,随后在webpack.config.js中配置plugins

// CleanWebpackPlugin需要进行解构 const {CleanWebpackPlugin} = require('clean-webpack-plugin') plugins: [ new CleanWebpackPlugin()
] 复制代码

自动生成HTML插件 html-webpack-plugin

const HtmlWebpackPlugin = require('html-webpack-plugin') plugins: [ new HtmlWebpackPlugin({ // 自定义生成的html文件的文件名 filename: 'about.html', // html文件的title和meta title: 'Webpack Plugin Sample', meta: { viewport: 'width=device-width' }, // 通过模板的方式获取想要修改的html的内容 template: './src/index.html' })
  ] 复制代码

拷贝那些不需要参与打包的资源文件到输出目录 copy-webpack-plugin

const CopyWebpackPlugin = require('copy-webpack-plugin') plugins: [ // 不建议开发阶段使用这个插件 new CopyWebpackPlugin({ // 拷贝文件的路径 patterns: ['public']
  })
] 复制代码
这里需要注意的是,当静态文件过多时,每次都会进行打包输出,会影响webpack的打包效率,所以这个插件建议在上线之前使用,不建议在开发阶段使用 复制代码

开发一个webpack插件

webpack中的插件是通过钩子机制进行实现的,自定义一个webpack插件时,默认是一个函数或者是一个包含apply方法的对象 这里我们做一个清除多余注释的插件

// 定义插件 class myPlugin {
  apply(compiler) { console.log('myPlugin 启动') // 这个钩子函数是在写入文件后执行 compiler.hooks.emit.tap('myPlugin', compilation => { // 此次打包的上下文 for(const name in compilation.assets) { if(name.endsWith('.js')) { // 写入文件的内容 const context = compilation.assets[name].source() // 通过正则表达式对其进行修改 const withoutComment = context.replace(/\/\*\*+\*\//g, '')
          compilation.assets[name] = { // 返回的文件内容 source: () => withoutComment, // 文件大小,webpack必须的参数 size: () => withoutComment.length
          }
        }
      }
    })
  }
} 复制代码

在下面进行引用,可以看到调用webpack打包时,后缀为.js的文件中的多余注释被清除掉了

增强webpack的开发体验

1.webpack自动编译

通过 yarn webpack --watch的方式监视文件,实现webpack自动编译

2.webpack自动刷新浏览器

通过broswer-sync实现

不过上面两种实现方式,在操作上需要使用两个,在操作上更加复杂了,这里我们通过webpack-dev-server实现上述两个功能 首先安装webpack-dev-server,随后运行这个工具

// 安装 yarn add webpack-dev-server --dev //使用 yarn webpack-dev-server // 自动打开浏览器 yarn webpack-dev-server --open 复制代码

这个依赖包可以自动启动一个本地服务器,并同时进行watch,大大提高了webpack的开发体验

需要注意的是,在使用这个依赖时,webpack为了提高效率,暂时不会对src目录下的文件进行打包输出到dist目录下,而是暂时存储到内存中 复制代码

由于上述copyWebpackPlugin这个插件不建议在开发阶段使用,当我们需要访问静态资源时,需要在配置文件中指定静态资源的路径

devServer: { contentBase: './public' }, 复制代码

通过对配置文件contentBase的设定,可以解决静态资源访问的问题

webpack-dev-server配置代理解决跨域问题

当CORS(跨域资源共享)无法满足webpack在本地访问api的跨域问题时,webpack-dev-server可以通过配置代理解决跨域问题,这里我们默认请求的是api/github.com中的users接口

devServer: { proxy: { '/api': { // http://localhost:8080/api/users => https://api.github.com/api/users target: 'https://api.github.com', // http://localhost:8080/api/users => https://api.github.com/users pathRewrite: { '^/api': '' }, // 不能使用localhost:8080 作为请求的主机名 changeOrigin: true }
  }
}, 复制代码

sourceMap

由于生产环境中运行的代码为开发阶段打包后转换的代码,两者的代码完全不同,在出现问题或者调试阶段,无法准确定位问题,这时,sourceMap解决了这个问题,在配置文件中定义devtool,并且设置为source-map,便可实现

devtool: 'source-map', 复制代码

同样,webpack支持12中sourceMap的定义方式

这些定义sourceMap的方式之间的区别体现在构建速度,二次打包速度,构建质量,和适用于生产4个方面,我们可以根据我们自己的需求合理的选择根据这些特点,建议大家在开发过程中选择cheap-module-eval-source-map,保证开发中每行代码不超过80个字符,并且依赖框架开发,对构建之前的源代码要求较高,同时对首次打包速度要求不高的情况下选择,在生产环境中,不建议生成sourceMap,或者使用nosources-source-map,保证自己源代码不暴露给其他人

webpack热替换 (HMR)

当代码进行变化时,webpack会自动进行编译,这时,浏览器页面会自动刷新,这样会导致一个问题,浏览器页面无法缓存当前页面的内容。HMR(Hot Module Replacement) 模块热替换便是webpack解决该问题的最有效的方式,应用运行过程中,实时替换某个模块,应用运行状态不受影响。

HMR是集成在webpack-dev-server中的,我们可以在webpack.config.js中对热更新进行配置,也可以在启动webpack-dev-server --hot实现热替换

devServer: { hot: true,
} 复制代码

这种方式配置可以实现HMR,但是在js文件中,浏览器还是无法保存修改前的内容自动刷新,所以我们可以在对应的代码中通过HMR的API进行代码编写,对其中的内容进行保存

let lastHeading = heading module.hot.accept('./heading.js', () => { // console.log('热更新启动') document.body.remove(lastHeading) const newHeading = createHeading() document.body.append(newHeading)
    lastHeading = newHeading
}) 复制代码

webpack多环境配置

在开发环境和生产环境中,我们的打包需求是不同的,所以我们可以通过多配置的方式实现多环境的打包工作

// webpack.common.js const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const { compilation } = require('webpack') const webpack = require('webpack') class myPlugin {
  apply(compiler) { console.log('myPlugin 启动') // 这个钩子函数是在写入文件后执行 compiler.hooks.emit.tap('myPlugin', compilation => { // 此次打包的上下文 for(const name in compilation.assets) { if(name.endsWith('.js')) { // 写入文件的内容 const context = compilation.assets[name].source() // 通过正则表达式对其进行修改 const withoutComment = context.replace(/\/\*\*+\*\//g, '')
          compilation.assets[name] = { // 返回的文件内容 source: () => withoutComment, // 文件大小,webpack必须的参数 size: () => withoutComment.length
          }
        }
      }
    })
  }
} module.exports = { mode: 'none', entry: './src/main.js', output: { filename: 'bundle.js', path: path.join(__dirname, 'dist'), // publicPath: 'dist/' }, devtool: 'source-map', devServer: { contentBase: './public', hot: true, //   hotOnly:true, proxy: { '/api': { // http://localhost:8080/api/users => https://api.github.com/api/users target: 'https://api.github.com', // http://localhost:8080/api/users => https://api.github.com/users pathRewrite: { '^/api': '' }, // 不能使用localhost:8080 作为请求的主机名 changeOrigin: true }
      }
    }, module: { rules: [
        { test: /.css$/,
          use: [ 'style-loader', 'css-loader' ]
        },
        { test: /.png$/,
          use: { loader: 'url-loader', options: { limit: 10 * 1024 // 10 KB }
          }
        }
      ]
    }, plugins: [ // 用于生成 index.html new HtmlWebpackPlugin({ title: 'Webpack Plugin Sample', meta: { viewport: 'width=device-width' }, template: './src/index.html' }), // 用于生成 about.html new HtmlWebpackPlugin({ filename: 'about.html' }), new myPlugin(), new webpack.HotModuleReplacementPlugin()
    ]

} // webpack.prod.js const { CleanWebpackPlugin } = require('clean-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') const common = require('./webpack.common') const {merge} = require('webpack-merge') module.exports = merge(common, { mode: 'production', plugins: [ new CleanWebpackPlugin(), new CopyWebpackPlugin({ // 拷贝文件的路径 patterns: ['public']
        })
    ]
}) 复制代码

这里我们新建一个webpack.common.js的文件,将一些公共配置写在里面,然后新建一个webpack.prod.js文件,在里面通过webpack内部的webpack-merge插件中的merge方法将其中的公共配置和production中所需要的的配置写进去

yarn webpack --config webpack.prod.js 复制代码

运行时通过yarn webpack --config webpack.prod.js,使用webpack.prod.js的配置进行打包

webpack DefinePlugin

为代码注入全局成员,在production下,插件默认启动起来,并且注入一个全局变量process.env.NODE_ENV

const webpack = require('webpack')

module.exports = {
    mode:'none',
    entry:'./src/main.js',
    output:{
        filename:'bundle.js',
    },
    plugins:[
        new webpack.DefinePlugin({
            API_BASE_URL: JSON.stringfy(''https://api.example.com)
        })
    ]
} 复制代码

webpack tree-shaking

tree-shaking是webpack中的重要功能,通过tree-shaking的方式将未引用的代码去掉,提高打包效率和代码体积,默认在production中开启,在其他环境中我们可以通过手动配置实现tree-shaking功能

const common = require('./webpack.common') const {merge} = require('webpack-merge') module.exports = merge(common, { mode: 'development', optimization: { // 模块只导出被使用的成员 usedExports: true, // 压缩输出结果 minimize: true }
}) 复制代码

通过配置文件中的optimization属性,实现tree-shaking功能,通过这个配置文件,我们可以大致了解tree-shaking的工作原理:在optimization中,usedExports作用是标记未使用的代码,minimize作用是删除这些标记的代码

合并代码

通过optimization中的concatenateModules合并代码

const common = require('./webpack.common') const {merge} = require('webpack-merge') module.exports = merge(common, { mode: 'development', optimization: { // 模块只导出被使用的成员 usedExports: true, // 尽可能合并每一个模块到一个函数中 concatenateModules: true, // 压缩输出结果 // minimize: true }
}) 复制代码

tree-shaking与babel

使用babel-loader,会导致tree-shaking失效的问题:因为tree-shaking前提是ES Modules,由webpack打包的代码必须使用ESM,为了转化ES中的新特性,会使用babel处理新特性,就有可能将ESM转化CommonJS,而我们使用的@babel/preset-env这个插件集合就会转化ESM为CommonJS,所以tree-shaking会不生效。但是在最新版babel-loader关闭了转换ESM的插件,所以使用babel-loader不会导致tree-shaking失效,如果你不确定babel-loader会不会使tree-shaking失效,那你可以在配置文件中进行配置,将module:false

rules: [
      { test: /\.js$/,
        use: { loader: 'babel-loader', options: { presets: [ // 如果 Babel 加载模块时已经转换了 ESM,则会导致 Tree Shaking 失效 // ['@babel/preset-env', { modules: 'commonjs' }] // ['@babel/preset-env', { modules: false }] // 也可以使用默认配置,也就是 auto,这样 babel-loader 会自动关闭 ESM 转换 ['@babel/preset-env', { modules: 'auto' }]
            ]
          }
        }
      }
    ]
  }, 复制代码

webpack sideEffects

webpack4.0中新增的新特性 - sideEffects 副作用,允许我们通过配置的方式标识代码是否有副作用,副作用指的是模块执行时除了导出成员之外所做的事情。如果没有副作用,则没有用到的模块则不会被打包。

// webpack.config.js optimization: { // 开启副作用 sideEffects:true } // package.json { "name": "03-plugin", "version": "0.1.0", "main": "index.js", "license": "MIT", "scripts": { "build": "webpack --config webpack.prod.js", "dev": "webpack --config webpack.dev.js" }, "devDependencies": { "clean-webpack-plugin": "^3.0.0", "copy-webpack-plugin": "^6.0.3", "css-loader": "^3.2.0", "file-loader": "^4.2.0", "html-webpack-plugin": "^3.2.0", "style-loader": "^1.0.0", "url-loader": "^2.2.0", "webpack": "^4.40.2", "webpack-cli": "^3.3.9", "webpack-dev-server": "^3.11.0", "webpack-merge": "^5.1.1" }, "sideEffect": false } 复制代码

需要注意的是,我们需要对两个地方进行配置,webpack.config.js中需要在optimization中配置sideEffects,并且在package.json中配置sideEffects为false,因为webpack在执行sideEffects时,会先到package.json中寻找这个属性,判断这些代码是否是有副作用的,设置为false后,sideEffects会开始工作,去除副作用代码进行打包

webpack代码分割

由于webpack是所有的代码打包到一起,如果打包文件过多,bundle会非常大。而并不是每个模块在启动时都是必要的,所以需要分包、按需加载。资源太大了不行,太碎了太零散了也不行。太大了会影响加载速度;太碎了会导致请求次数过多,因为在目前主流的HTTP1.1有很多缺陷,如同域并行请求限制、每次请求都会有一定的延迟,请求的Header浪费带宽流量。所以模块打包时有必要的。

多入口打包

适用于传统的多页面应用,一个页面对应一个打包入口,不同页面公共部分单独提取。

const HtmlWebpackPlugin = require('html-webpack-plugin') 
const {CleanWebpackPlugin} = require('clean-webpack-plugin')

module.exports = {
    mode:'none',
    entry:{
       index: './src/index.js',
       album: './src/album.js'
    },
    output:{
        filename:'[name].bundle.js'
    },
    module:{
        rules:[
            {
                test:/\.css$/,
                use:[
                    //style-loader是将css-loader处理后的结果,通过style的形式追加到页面上
                    'style-loader',
                    'css-loader'
                ]
            }
        ]
    },
    plugins:[
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            filename:'index.html',
            chunks:['index'] //引入的js文件
        }),
        new HtmlWebpackPlugin({
            filename:'album.html',
            chunks:['album'] //引入的js文件
        }),
    ]

} 复制代码

这里的配置文件中,将入口文件entry设置为对象,通过对应的key值声明不同的入口,同时在下面的htmlWebpackPlugin插件中,由于该插件会默认将所有html打包到一起,所以设置chunks属性,设置为引用的js文件,这样就实现了多入口打包

提取公共部分

如果某一个文件被多个文件引用,则可以使用配置的方式对公共部分进行提取,需要在配置文件中配置optimization

optimization: {
      splitChunks: {
          chunks: 'all'
      }
} 复制代码

动态导入和魔法注释

需要用到某个模块时,再加载这个模块,动态导入的模块会被自动分包。通过动态导入生成的文件只是一个序号,可以使用魔法注释指定分包产生bundle的名称。相同的chunk名会被打包到一起。

import(/* webpackChunkName: 'component' */'./post/posts').then({default: posts}) => {
  mainElement.appendChild(posts())
} 复制代码

webpack MiniCssExtractPlugin

提取css到单个文件,通过这个插件可以实现css的按需加载。使用时需要先安装该插件

const MiniCssExtracPlugin = require('mini-css-extract-plugin') plugins: [ new MiniCssExtracPlugin()
 ] module: { rules: [
            { test: /\.css$/,
                use: [ //style-loader是将css-loader处理后的结果,通过style的形式追加到页面上 //'style-loader', MiniCssExtracPlugin.loader, 'css-loader' ]
            }
        ]
 } 复制代码

webpack OptimizeCssAssetsWebpackPlugin

webpack默认只支持对js文件的压缩,但是对其他为文件没有作用,所以我们需要使用插件对其进行压缩。

const OptimizeCssAssetWebpackPlugin = require('optimize-css-assets-webpack-plugin') const TerserWebpackPlugin = require('terser-webpack-plugin') optimization: { // webpack建议在minimizer中定义该插件,但是在使用中,自定义的插件会默认覆盖掉webpack // 本身的压缩内容,所以需要重新引用js中的压缩插件  minimizer: [ new TerserWebpackPlugin(), new OptimizeCssAssetWebpackPlugin()
        ]
      }, plugins: [ // webpack不建议在plugins中使用该插件,这样会将所有css文件进行压缩 new OptimizeCssAssetWebpackPlugin()
    ] 复制代码

webpack不建议在plugins中使用该插件,这样会将所有css文件进行压缩,所以通常我们会在minimizer中定义该插件,但是在使用中,自定义的插件会默认覆盖掉webpack本身的压缩内容,所以需要重新引用js中的压缩插件 terser-webpack-plugin

hash

webpack打包后,当需要刷新页面时会存在缓存问题,所以webpack可以通过在文件名中定义hash来解决缓存问题,目前共有三种方式定义,在定义hash后,可以在后面通过:数字的形式定义hash的长度 1、hash

output: { filename: '[name]-[hash:8].bundle.js' }, 复制代码

2、chunkhash

output: { filename: '[name]-[chunkhash:8].bundle.js' }, 复制代码

3、contenhash

output: { filename: '[name]-[contenhash:8].bundle.js' }, 复制代码

以上就是webpack中学习的所有内容,当然,这只是webpack的冰山一角,具体的更多可以通过官方文档进行学习

eslint

eslint是目前最为主流的javascript lint 工具,监测js代码质量,通过eslint可以很容易统一开发者的编码风格,同时,可以帮助开发者提升编码能力

安装

初始化项目 安装ESLint模块为开发依赖 通过CLI命令验证安装结果

eslint使用

编写“问题”代码 使用eslint执行检测 完成eslint实用配置

eslint配置

在使用eslint之前,需要对eslint进行初始化配置,运行yarn eslint --init执行初始化配置

只检查语法错误

检查语法错误和问题代码

检查语法和问题代码,并校验代码风格

模块化的类型

框架的选择

是否使用ts(typescript)

代码最终的运行环境

定义项目的代码风格:

目前主流的js风格

询问一些问题获取你的代码风格

根据你的js文件推断出你的风格

配置文件的存放格式

对配置文件进行设置之后,eslint会根据你所选择的标准规范下载对应的npm依赖

注意,在对项目命名时,不要起与eslint相同的项目名,会在安装依赖时报错(好坑!!!) 复制代码

随后可以通过yarn eslint 指定文件进行校验,在eslint校验的过程中,首先eslint会先捕获到异常语法代码的问题,因为这些问题在运行时会阻碍进程,将异常语法代码修改后,eslint会对问题代码和样式进行校验,通过 --fix可以自动将样式统一,问题代码根据提示进行手动修改

eslint配置文件解析

module.exports = { env: { browser: true, es2020: true }, extends: [ 'standard' ], parserOptions: { ecmaVersion: 11 }, rules: {
  }
} 复制代码

env中,标记了eslint的环境,例如,browser设置为true,意味着当前代码运行环境是浏览器中,我们可以通过这个配置,来使用对应浏览器中的全局API,这里我们可以配置多种类型的环境,全部设置为true,互不影响,下面是可以设置的环境

extends代表的是共享配置,由于是数组,可以对其配置多个共享配置

parserOptions代表的是ES版本的语法,这里的ecmaVersion是11,代表可以使用es11的语法,可以在这里设置指定的es规范,这里需要注意的是parserOptions这里只是影响语法检测,不影响可用,具体成员根据环境定义

rules中定义了一些自定义规则

eslint配置注释

通过 // eslint-disable-line 忽略当前行的规范校验

eslint结合自动化工具

在eslint工作时,一般是针对js代码的检测,所以,在自动化工具构建js之前,我们可以通过在babel处理之前首先处理eslint检测

这里拿gulp来举例,这里需要安装eslint和gulp-eslint

const script = () => { return src(config.build.paths.scripts, { base: config.build.src, cwd: config.build.src })
        .pipe(plugins.eslint())
        .pipe(plugins.eslint.format())
        .pipe(plugins.eslint.failAfterError())
        .pipe(plugins.babel({ presets: [require('@babel/preset-env')] }))
        .pipe(dest(config.build.temp))
        .pipe(bs.reload({ stream: true }))
} 复制代码

eslint 结合webpack

webpack集成eslint是通过loader机制来实现的,所以在结合webpack时,我们需要在webpack.config.js中对rule进行配置

{ test: /.js$/, // 正则表达式,匹配打包过程的文件后缀 use: 'eslint-loader', // 先执行 enforce: 'pre' },

作者:么耶咩
链接:https://juejin.im/post/6863838861089964045
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关新闻

从ES6到ES10的新特性万字大总结(不得不收藏)

2019.12.19

1115

ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会)在标准ECMA-262中定义的脚本语言规范。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,但实际上后两者是ECMA-262标准的实现和扩展。

我奶奶看了都点赞的前端脚手架教程

2020.12.31

746

一说起脚手架,你肯定会想到vue-cli。有些人看到vue-cli那个酷炫的功能和交互就会打退堂鼓。如果我告诉你,我们只需要具备nodejs基础就能写出vue-cli那样的功能来,你是不是稍微会兴奋点呢?接下来跟着我的步伐一点一点揭开脚手架的面纱吧。

substr_replace如何替换多个字符串不同位置不同长度的子串

2019.01.23

3615

我们在做微信公众号开发,或者微信小程序开发时,经常要处理一些字符串,那么像字符串替换之类,今天简单说一下substr_replace的使用方式。