之前演示了很多示例,讲的都是
webpack处理各种类型的文件,处理方式就是根据文件类型、输出用途来进行各种配置,这次就来讲讲这些配置背后到底有哪些门道。
webpack配置结构详解
在之前的案例中,webpack的配置导出的都是对象形式,其实它还可以是数组和函数。
使用数组
数组的使用方式也很简单, 就是多个对象形式或者函数形式的配置合并成数组,如下
const path = require('path'); module.exports = [ { entry: './src/main.js', output: { path: path.resolve(__dirname, './dist'), filename: "[name].js" } }, function() { return { entry: { index: './src/main.js' }, output: { path: path.resolve(__dirname, './dist'), filename: "[name].js" } } } ]
上面的配置经过构建之后就会生成两个文件,需要注意的是两个对象的配置在构建的时候会创建多个Compilation实例来并行执行构建工作,就算两个入口文件指向的是同一个文件,他们的构建、解析流程都无法复用,一般使用数组构建的方式的情况都是生成多种产物,例如之前讲过的npm的library,他最终的产物需要有ECM模块,还需要有CMD、UMD模块,那么配置就是下面这样的:
const path = require('path'); module.exports = [ { entry: './src/main.js', output: { path: path.resolve(__dirname, './dist'), filename: "[name]-amd.js", library: { type: "amd" } }, name: 'amd', }, { entry: './src/main.js', output: { path: path.resolve(__dirname, './dist'), filename: "[name]-cmd.js", library: { type: "commonjs" } }, name: 'commonjs', }, ]
上面的配置大多数都是重复的,这里就可以使用到webpack-merge来简化配置,复用配置逻辑
const path = require('path'); const {merge} = require('webpack-merge'); const baseConfig = { mode: 'production', entry: './src/main.js', output: { path: path.resolve(__dirname, './dist'), filename: "[name].js", }, }; module.exports = [ merge(baseConfig, { output: { library: { type: "amd" } }, name: 'amd' }), merge(baseConfig, { output: { library: { type: "commonjs" } }, name: 'commonjs' }), ]
提示:使用配置数组时,还可以通过
--config-name参数指定需要构建的配置对象,例如上例配置中若执行npx webpack --config-name='amd',则仅使用数组中name='amd'的项做构建。
使用函数
函数配置的需求就是函数返回值需要返回webpack配置对象或配置数组,或者是一个Promise,webpack会传递两个环境参数对象:
/** * * @param env 通过 --env 传递的命令行参数,适用于自定义参数,例如 * npx webpack --env prod { prod: true } * --env platform=app --env production { platform: "app", production: true } * --env foo=bar=app { foo: "bar=app"} * * @param argv 命令行 Flags 参数,支持 entry/output-path/mode/merge 等 * npx webpack --mode=development {mode: 'development'} * --entry=index.js --mode=development {entry: 'index.js', mode: 'development'} */ module.exports = function (env, argv) { return { mode: argv.mode || 'production', entry: './src/main.js', output: { path: path.resolve(__dirname, './dist'), filename: "[name].js", }, } }
一般的情况下我们还是使用对象形式的方式来配置webpack,具体怎么使用还是看各自的需求,例如你发布的一个npm包可以在node环境下使用,也可以在web环境下使用,在node环境下一般使用的都是cmd的require方式,web端一般都是import的方式,但是在node环境下的包会使用一些node的功能,web端会使用浏览器对象,这个时候就可以对象数组的方式进行配置。
环境治理策略
一般一个项目会区分不同的环境,例如开发环境、生产环境、测试环境等,这些环境下面大多是相同的配置,只有少数不同的配置,例如开发环境需要使用webpack-dev-server,测试环境需要Soucemap定位问题,生产环境啥也不需要,就要能稳定的运行,兼容不同的浏览器。
这一块我在之前的文章讲过,就是配置合并,这里的配置合并用到的依然是webpack-merge,这里思考一个问题,为什么不使用Object.assign,也不卖关子了,Object.assign是属性覆盖,不是属性合并。
环境治理方案有很多,最常用的还是使用不同的配置文件,然后通过webpack-merge进行配置合并,最后通过--config的参数来制定输出什么环境的文件,这一块可以看看我之前写过的配置合并,突然感觉自己当时有点操之过急,这篇课程的定位可能就不是新手看的。
核心配置项汇总
webpack的配置项高达上百种,如果每个都要学习那学习量是在是太大了,于是作者指明了一些核心:
entry:声明项目入口文件,Webpack 会从这个文件开始递归找出所有文件依赖;output:声明构建结果的存放位置;target:用于配置编译产物的目标运行环境,支持web、node、electron等值,不同值最终产物会有所差异;mode:编译模式短语,支持development、production等值,Webpack 会根据该属性推断默认配置;optimization:用于控制如何优化产物包体积,内置 Dead Code Elimination、Scope Hoisting、代码混淆、代码压缩等功能;module:用于声明模块加载规则,例如针对什么类型的资源需要使用哪些 Loader 进行处理;plugin:Webpack 插件列表。
entry
entry是webpack的入口配置,必须明确声明,属性值可以是字符串、对象、数组、函数形式,这里的配置可以说是很多变的:
// 字符串 module.exports = { entry: './main.js' }; // 对象,对象形式的值可以指定为字符串、数组[string]、对象(对象会有额外的配置)、函数 module.exports = { entry: { // 字符串形态 home: './home.js', // 数组形态 shared: ['react', 'react-dom', 'redux', 'react-redux'], // 对象形态 personal: { import: './personal.js', filename: 'pages/personal.js', dependOn: 'shared', chunkLoading: 'jsonp', asyncChunks: true }, // 函数形态 admin: function() { return './admin.js'; } } }; // 数组,数组可以是字符串,对象、函数 module.exports = { entry: [ './main.js', { main: './main.js' }, function () { return './main.js' } ] }; // 函数,返回值可以是字符串、对象、数组 module.exports = { entry: function () { return './main.js' } };
这其中,entry属性值中,属性值的项为对象是最复杂的,例如上面的对象形态的personal属性值也是对象形态的,支持如下配置属性:
import:声明入口文件,支持路径字符串或路径数组(多入口);dependOn:声明该入口的前置依赖 Bundle;runtime:设置该入口的 Runtime Chunk,若该属性不为空,Webpack 会将该入口的运行时代码抽离成单独的 Bundle;filename:效果与 output.filename 类同,用于声明该模块构建产物路径;library:声明该入口的 output.library 配置,一般在构建 NPM Library 时使用;publicPath:效果与 output.publicPath 相同,用于声明该入口文件的发布 URL;chunkLoading:效果与 output.chunkLoading 相同,用于声明异步模块加载的技术方案,支持false/jsonp/require/import等值;asyncChunks:效果与 output.asyncChunks 相同,用于声明是否支持异步模块加载,默认值为true。
其中
dependOn的作用是将该入口文件作为一个访客形式的存在来生成产物,它的属性值指向的就是要访问的对象,什么意思呢?就是指定了这个属性,例如这个属性值是main(main指向的是同在入口文件中配置的入口文件),这个main使用的东西,在当前模块也有使用,那么当前模块的生成产物中就不包含这个东西,直接找main要,我将这种行为称为访客形式应该没问题吧。
runtime就是将运行时相关内容单独打个包,生成一个单独的bundle,运行时相关的又是个啥?就是例如你在代码中使用的require,这个浏览器是没得这个函数的,你的一些相关依赖包里面也没有安装这个相关的,那就只能由webpack给你提供喽,提供就需要注入代码,如果这种相关的运行时函数使用多了,就会很大,就需要剥离。
使用 output 声明输出方式
output在构建npm包的那一节讲过一些相关的,这一节就详细讲讲,他支持的配置项如下:
- output.path:声明产物放在什么文件目录下;
- output.filename:声明产物文件名规则,支持
[name]/[hash]等占位符; - output.publicPath:文件发布路径,在 Web 应用中使用率较高;
- output.clean:是否自动清除
path目录下的内容,调试时特别好用; - output.library:NPM Library 形态下的一些产物特性,例如:Library 名称、模块化(UMD/CMD 等)规范;
- output.chunkLoading:声明加载异步模块的技术方案,支持
false/jsonp/require等方式。 - 等等。
output.filename可以是字符串或函数,函数需要返回一个字符串,字符串可以有占位符。
output.publicPath对于web项目来说很常用,很多时候构建出来的东西放到服务器发现404了,一般就是这个属性没有配置好。
output.clean记得我最开始玩webpack的时候还是需要使用一个清除文件得插件来实现这个功能。
其他的就没什么好说的了。
使用 target 设置构建目标
target属性在构建electron的时候使用过,主要是用于将构建的目标是支持什么平台的,目前支持可选的属性值。
突然觉得官网讲的比我详细,比课程详细,所以这里就放出官网的链接了。
使用 mode 短语
在我们执行npx webpack的时候,如果没有指定mode属性,那么控制台将会抛出警告,告诉你没有指定mode属性,但是还是给你构建出内容了。
指定该属性,webpack会根据提供的属性值,然后启用内部定义好的默认值进行一系列的优化处理,如果没有设置,那么默认就会使用production,详细还是看官网:mode
总结
webpack的配置有很多种形态,每一种形态都有对应的使用场景,例如导出的配置可以使用异步函数,这样就可以通过服务端来获取配置,例如mode属性,可以区分开发环境和生产环境,这个大家应该很常用,然后就是各种搭配使用的方式,使其灵活多变,webpack难就难在配置多,难就难在构建一个合适的环境具体要怎么搭配才能更好的提高生产力。
这一节没有写多少示例代码, 因为场景太多,搭配不过来,有兴趣的自行尝试。