webpack.config系列中较为基础的部分,稍微高阶点的都会在 进阶篇里
配置文件
一般情况下,webpack 的配置文件是 JavaScript 文件,文件内导出了一个 webpack 配置的对象
快速生成配置文件
npx webpack-cli init
使用不同的配置文件
// package.json
"scripts": {
"build": "webpack --config prod.config.js"
}
使用其他编程语言编写配置文件
其他导出格式
我们知道,配置文件一般是导出一个对象,但也可以导出其他格式
导出函数
一般是用于区分开发/生产环境(只是其中的一个方法)。该函数包括两个参数,一是环境(参考environment 选项文档)、二是webpack配置项
module.exports = function(env, argv) {
+ return {
+ mode: env.production ? 'production' : 'development',
+ devtool: env.production ? 'source-map' : 'eval',
plugins: [
new TerserPlugin({
terserOptions: {
+ compress: argv.mode === 'production' // only if `--mode production` was passed
}
})
]
+ };
};
导出Promise
当需要异步加载配置变量时,webpack 将执行函数并导出一个配置文件,同时返回一个 Promise
module.exports = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
entry: './app.js',
/* ... */
});
}, 5000);
});
};
只有通过 webpack 命令行工具返回的
Promise才生效
导出多个配置
可以导出多种配置,当运行 webpack 时,所有配置项都会构建。比如,对于多 targets(如 AMD 和 CommonJS)构建 library时会非常有用
module.exports = [
{
output: {
filename: './dist-amd.js',
library: {
name: 'amd',
type: 'amd',
}
},
name:'amd',
entry: './app.js',
mode: 'production',
},
{
output: {
filename: './dist-commonjs.js',
library: {
name: 'commonjs',
type: 'commonjs',
}
},
name:'commonjs',
entry: './app.js',
mode: 'production',
},
];
如果你只传了一个
--config-name名字标识,webpack 将只会构建指定的配置项
- 如果你的某个配置依赖于另一个配置的输出,你可以使用一个
dependencies列表指定一个依赖列表
module.exports = [
{
name: 'client',
target: 'web',
// …
},
{
name: 'server',
target: 'node',
dependencies: ['client'],
},
];
- 如果你导出了多个配置,你可以在配置中使用
parallelism选项来指定编译的最大并发数
module.exports = [
{
//config-1
},
{
//config-2
},
];
module.exports.parallelism = 1;
Notice
- 整个配置中我们使用 Node 内置的 path 模块,并在它前面加上 __dirname 这个全局变量。可以防止不同操作系统之间的文件路径问题,并且可以使相对路径按照预期工作
name
配置的名称。当加载不同的配置时会被使用
context
基础目录,绝对路径,用于从配置中解析入口点(entry point)和 加载器(loader)
默认是Node.js 进程的当前工作目录
target
作用
由于 JavaScript 既可以编写服务端代码也可以编写浏览器代码,所以 webpack 提供了多种部署 target
默认值为 "browserslist",如果没有找到 browserslist 的配置,则默认为 "web"
设置
module.exports = {
target: 'node',
};
示例中,target 设置为 node,webpack 将在类 Node.js 环境编译代码。(使用 Node.js 的 require 加载 chunk,而不加载任何内置模块,如 fs 或 path)。
每个 target 都包含各种 deployment(部署)/environment(环境)特定的附加项,以满足其需求。 可指定
node或者electron的版本,如'node12.18'
多target
首先,个人不太推荐这么用!
当传递多个目标时,将使用共同的特性子集,也就是说,要它们都拥有的特性,才能够使用
下例中,Webpack 将生成 web 平台的运行时代码,并且只使用 ES5 相关的特性。
module.exports = {
// ...
target: ['web', 'es5'],
};
注意:目前并不是所有的 target 都可以进行混合。
mode
打包模式,默认是生产模式,生产模式下会做代码压缩,摇树等操作。
- 设置 mode 后改变全局变量(业务js中也可以使用)
process.env.NODE_ENV的值 - 设置 mode 后等同于在中加入了
webpack.DefinePlugin插件
module.exports = function(env, argv) {
+ mode: env.production ? 'production' : 'development',
- devtool: env.production ? 'source-map' : 'eval',
- plugins: [
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development|production") })
- ]
+ };
};
mode可以在 webpack.config.js 里配置,也可以通过CLI参数传递,如 webpack --mode=development
选项
有三个可选值,分别是"none","development","production",默认为 "production"
- none:不开启任何优化选项
- development:设置后会启用 NamedChunksPlugin 和 NamedModulesPlugin,也就是这些插件,webpack 里就不用写了
module.exports = function(env, argv) {
+ mode: 'development',
- devtool: 'eval',
- plugins: [
- new webpack.NamedChunksPlugin(),
- new webpack.NamedModulesPlugin(),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") })
- ]
+ };
};
NamedModulesPlugin:原本webpack并不会给打包的模块加上姓名,一般都是按照序号来,从0开始,然后加载第几个模块。没有
NamedModulesPlugin,模块就是一个数组,引用也是按照在数组中的顺序引用,新增减模块都会导致序号的变化。有了NamedModulesPlugin,模块都拥有了姓名,而且都是独一无二的key,不管新增减多少模块,模块的key都是固定的
NamedChunksPlugin:与 NamedModulesPlugin 相似,原来的chunks也是序号命名,开启这个plugin后会有独一无二的命名
- production:设置后会启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin(也就是webpack里不用再设置了!)
UglifyJsPlugin :混淆/压缩JS,并删除未引用代码,并压缩。不过这个可以在
optimization.minimize设置
ModuleConcatenationPlugin :作用域提升。此插件仅适用于由 webpack 直接处理的 ES6 模块。在使用转译器时,你需要禁用对模块的处理(例如 Babel 中的 modules 选项)
NoEmitOnErrorsPlugin :在输出阶段时,遇到编译错误是否跳过
FlagIncludedChunksPlugin:防止chunks多次加载
OccurrenceOrderPlugin :按照chunk引用次数来安排出现顺序,因为这让经常引用的模块和chunk拥有更小的id
SideEffectsFlagPlugin :副作用是否打包,优化块会讲
Notice
如果要根据 webpack.config.js 中的 mode 变量更改打包行为,则必须将配置导出为函数,而不是导出对象
var config = {
entry: './app.js',
//...
};
module.exports = (env, argv) => {
if (argv.mode === 'development') {
config.devtool = 'source-map';
}
if (argv.mode === 'production') {
//...
}
return config;
};
这样就可以通过cli中 mode 参数来传入变量 "build": "webpack --mode=production"
entry
- 字符串
module.exports = {
entry: './src/file.js',
};
- 数组
module.exports = {
entry: ['./src/file_1.js', './src/file_2.js'],
};
- 对象
entry 对象里的key,将作为
name传入 output中。
在下列例子中,假设 home.js 和 main.js 都依赖 'react', 'react-dom',如果单纯进行入口隔离(即两个entry home 和 main),那么打包出的bundle都会有一份 'react', 'react-dom'。所以我们将 'react', 'react-dom'抽离出来共享:
module.exports = {
entry: {
home: {
import: './home.js',
dependOn: 'shared',
},,
shared: ['react', 'react-dom'],
main: {
import: './main.js',
filename: 'pages/personal.js',
dependOn: 'shared',
chunkLoading: 'jsonp',
asyncChunks: true, // Create async chunks that are loaded on demand.
layer: 'name of layer', // set the layer for an entry point
},
},
};
filename
默认情况下,入口 chunk 的输出文件名是从 output.filename 中提取出来的,但你可以为特定的入口指定一个自定义的输出文件名,如上面的 filename: 'pages/personal.js',
dependOn
默认情况下,每个入口 chunk 保存了全部其用的模块(modules)。使用 dependOn 选项你可以与另一个入口 chunk 共享模块,如上面的 dependOn: 'shared',这样,personal 这个chunk 就不会包含 shared拥有的模块了
dependOn 选项也可以为数组 dependOn: ['shared', 'shared2']
动态entry
如果传入一个函数,那么它将会在每次 make (webpack启动和监听文件变化时,make事件都会触发) 事件中被调用
module.exports = {
//...
entry: () => './demo',
// or下面这种
// entry: () => new Promise((resolve) => resolve(['./demo', './demo2'])),
};
应用场景
分离 app(应用程序) 和 vendor(第三方库) 入口
这样你就可以在 vendor.js 中存入未做修改的必要 library 或文件(例如 Bootstrap, jQuery, 图片等),然后将它们打包在一起成为单独的 chunk。内容哈希保持不变,这使浏览器可以独立地缓存它们,从而减少了加载时间。这是webpack4以下版本常用做法,webpack4以上请使用optimization.splitChunks
webpack.config.js
module.exports = {
entry: {
main: './src/app.js',
vendor: './src/vendor.js',
},
};
webpack.prod.js
module.exports = {
output: {
filename: '[name].[contenthash].bundle.js',
},
};
webpack.dev.js
module.exports = {
output: {
filename: '[name].bundle.js',
},
};
多页面应用程序
在多页面应用程序中,每个 HTML 页面都有一个入口起点
module.exports = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js',
},
};