这一节内容主要介绍详细的webpack配置信息,毕竟“webpack高级配置工程师”是每个前端er的目标
首先要知道webpack的核心概念
- entry:
entry
是配置模块的入口,可抽象成输入,Webpack 执行构建的第一步将从入口开始搜寻及递归解析出所有入口依赖的模块。 - output:
output
配置如何输出最终想要的代码。 - mode:通过选择
development
,production
或none
之中的一个,来设置mode
参数,可以启用 webpack 内置在相应环境下的优化。其默认值为production
- loader:webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件。
- plugin:loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
entry
entry的类型可以为字符串、数组或者对象
类型 | 例子 | 含义 |
string | './app/entry' |
入口模块的文件路径,可以是相对路径。 |
array | ['./app/entry1', './app/entry2'] |
入口模块的文件路径,可以是相对路径。 |
object | { a: './app/entry-a', b: ['./app/entry-b1', './app/entry-b2']} |
配置多个入口,每个入口生成一个 Chunk |
Webpack 会为每个生成的 Chunk 取一个名称,Chunk 的名称和 Entry 的配置有关:
- 如果
entry
是一个string
或array
,就只会生成一个 Chunk,这时 Chunk 的名称是main
; - 如果
entry
是一个object
,就可能会出现多个 Chunk,这时 Chunk 的名称是object
键值对里键的名称。
output
output是一个
object,里面包含了输出文件名称、路径等信息,如下
output: { filename: '[name].js', path: path.resolve(__dirname, dist), publicPath: 'https://cdn.abcd.com/assets/', // libraryTarget: var, // library: 'test' } 复制代码
- filename:输出的文件名,
[name]
相当于是占位符,可以在编译时替换为内置的 name 变量,除此之外还有id(唯一标识,从0开始)、hash(唯一标识的哈希值),chunkhash(chunk内容的hash值),使用哈希值是可以指定长度如[hash:8].js
,默认长度为20 - path:配置输出文件存放在本地的目录,必须是 string 类型的绝对路径。通常通过 Node.js 的
path
模块去获取绝对路径 - publicPath:复杂的项目里可能会有一些构建出的资源需要异步加载,加载这些异步资源需要对应的 URL 地址。publicPath就是用于设置这些URL的,比如上面的配置打包之后的html中就会引入
<script src="https://cdn.abcd.com/assets/main.js"></script> 复制代码
- libraryTarget和library:当构建一个可以被其他模块导入的库时就需要用到这两个配置。libraryTarget配置以何种方式导出,library配置导出库的名字。默认以var方式导出,导出之后的内容是一个自执行函数,此外还有
- commonjs(按照commonjs标准,exports['libraryName']导出,用require('dep')['libraryName']导入)、
- commonjs2(与commonjs区别在于增加了module.exports,libraryTarget 为
commonjs2
时,配置 library 将没有意义)、 - this(编写的库将通过
this
被赋值给通过library
指定的名称)、 - window(编写的库将通过
window
被赋值给通过library
指定的名称)、 - global(编写的库将通过
global
被赋值给通过library
指定的名称)
- libraryExport: 配置要导出的模块中哪些子模块需要被导出,它只有在 libraryTarget 被设置成
commonjs
或者commonjs2
时使用才有意义
loader
webpack处理模块主要依赖的时loader,使用loader来解析不同的文件,最后统一输出
module: { rules: [ { test: /\.less$/, // 正则匹配 use: ['style-loader', 'css-loader'], // 要使用的loader exclude: path.resolve(__dirname, 'node_modules'), // 不包含的目录 // include: path.join(__dirname, '/src'), // 包含的目录 },{ test: /\.less$/, loader: 'less-loader', exclude: path.resolve(__dirname, 'node_modules'), enforce: 'pre', // 有多个规则时,通过enfore可以控制执行循序,pre前置>行内>普通>post后置 } ] }, 复制代码
- relues:loader的配置放在module下的rules中,rules可以是一个数组也可以是一个对象,还可以是一个字符串,通过不同的匹配规则(test、incluede、exclude)来解析不同的模块
匹配到文件之后使用loader或者use来声明要使用的loader,当需要有多个loader处理时使用use数组,可以为字符串数组(可以通过'loaderName?param'的形式给loader传递参数,废弃
),当参数过多时,也可以使用对象来具体描述loader
use: [ { loader:'babel-loader', options:{ cacheDirectory:true, }, }, ] // 等价于 use: ['babel-loader?cacheDirectory'] 复制代码
- noParse:使用noParse可以告知webpack忽略掉一些不必要解析的库,比如jquery等,让webpack 去解析这些文件耗时又没有意义,忽略解析可以提高性能
module: { noParse: /jquery/ } 复制代码
- parser:和noParse区别在于,noParse只能控制文件不被解析,而parser可以精确到语法
module: { rules: [ { test: /\.js$/, use: ['babel-loader'], parser: { amd: false, // 禁用 AMD commonjs: false, // 禁用 CommonJS } }, ] } 复制代码
plugin
插件目的在于解决 loader 无法实现的其他事。由于插件可以携带参数/选项,必须在 webpack 配置中,向 plugins
属性传入一个 new 实例。
几乎所有 Webpack 无法直接实现的功能都能在社区找到开源的 Plugin 去解决
devServer
devServer可以显著体改开发效率
- hot:模块热替换功能,默认的行为是在发现源代码被更新后会通过自动刷新整个页面来做到实时预览,开启模块热替换功能后将在不刷新整个页面的情况下通过用新模块替换老模块来做到实时预览。
- contentBase:配置 DevServer HTTP 服务器的文件根目录,默认情况下为当前执行目录,通常是项目根目录。
- headers:可以在 HTTP 响应中注入一些 HTTP 响应头。
- host:DevServer 服务监听的地址,默认值是127.0.0.1,如果想让局域网中的其他机器可以访问,可以设置0.0.0.0
- port:端口号,默认使用 8080 端口。
- allowHosts:一个白名单列表,只有 HTTP 请求的 HOST 在列表里才正常返回。
- https:开启https,会自动的为你生成一份 HTTPS 证书。也可以手动配置
devServer:{ https: { key: fs.readFileSync('path/to/server.key'), cert: fs.readFileSync('path/to/server.crt'), ca: fs.readFileSync('path/to/ca.pem') } } 复制代码
- open:编译完成自动打开浏览器访问。
- openPage:设置open打开的URL。
- compress:是否开启gzip压缩。
- proxy:可以将特定的url转发到另一台服务器
proxy: { '/api': { target: 'http://localhost:3000/api', // 如果要代理 websockets ws: true, // 将主机标头的原点更改为目标URL changeOrigin: true, secure: false, pathRewrite: { [`^/api`]: '' } } } // 在访问/api/users时,就会转发到http://localhost:3000/api/users 复制代码
resolve
可以设置设置模块如何被解析
- alias:别名,可以给对应的路径设置别名来简化书写。例如
resolve:{ alias:{ @: './src' // 可以在代码中通过'@'来代替src,原来的./src/views可以用@/views访问 } } 复制代码
- extensions:在导入语句没带文件后缀时,webpack会按照配置的后缀顺序去寻找文件
resolve: { extensions: ['.js', '.json'] // 导入import './foo'时,会先寻找foo.js,如果没有找到会再去找foo.json } 复制代码
- modules:告知webpack去哪些目录下寻找依赖
resolve: { modules:['./src/components','node_modules'] // 导入require('foo')时,会优先去'./src/components'目录下寻找,然后再去node_modules下寻找 } 复制代码
- descriptionFiles:配置描述第三方模块的文件名称,默认是
package.json
文件。 - enforceExtension:配置导入文件是否必须带后缀,默认为false。如果配置为true,导入时就必须
import foo from './foo.js'
- enforceModuleExtension:与enforceExtension搭配使用,一些第三方依赖包没有携带文件后缀,通过设置
enforceModuleExtension: false
来兼容第三方模块
其他
module.export = { devtool: 'source-map', // 控制是否生成source-map watch: true, // 是否开启文件监听 watchOptions: { // 不监听的文件或文件夹,支持正则匹配 // 默认为空 ignored: /node_modules/, // 监听到变化发生后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高 // 默认为 300ms aggregateTimeout: 300, // 默认每隔1000毫秒询问一次 poll: 1000 }, externals: { // 把导入语句里的 jquery 替换成运行环境里的全局变量 jQuery jquery: 'jQuery' }, resolveLoader:{ // 去哪个目录下寻找 loader modules: ['node_modules'], // 入口文件的后缀 extensions: ['.js', '.json'], // 指明入口文件位置的字段 mainFields: ['loader', 'main'] } } 复制代码
其他配置类型
webpack.config.js可以导出一个对象,这是最常见的形式,此外webpack还可以导出一个函数,这个函数接收两个参数环境变量env和命令参数argv,导出形式如下
const UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin'); module.exports = function (env = {}, argv) { const plugins = []; const isProduction = env['production']; // 在生产环境才压缩 if (isProduction) { plugins.push( new UglifyJsPlugin() ) } return { plugins: plugins, // 在生成环境不输出 Source Map devtool: isProduction ? undefined : 'source-map', }; } 复制代码
除此之外还可以导出一个Promise,用于不能以同步的方式返回一个描述配置的 Object时
module.exports = function(env = {}, argv) { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ // ... }) }, 5000) }) }