基于webpack 4.0版本
阅读 webpack文档 获取更详细的信息。
文中说明的属性,是本人在项目中用到的属性,比较少使用的属性,将不会进行说明。
1. webpack基础
mode :
打包的环境检测,值是production 和 developemnt。当然如果要实现更细致的环境判断,可以设置自定义的环境常量(如REACT_ENV)。
mode: "production" || "development"
entry
打包入口,默认为 ./src
。值类型可以是字符串,数组,对象。常见的单页面开发入口的值是字符串(或者对象,如果vendor写在这里)。
entry: "./main.js"
entry: ["./main.js", "./dll/react.js"]
entry: {
pageA: "./pageA.js",
pageB: "./pageB.js"
}
output
输出的文件。
output: {
// 输出目标路径
path: path.resolve(__dirname, 'dist'),
// 输出文件名称,name 对应入口文件的 name ,chunkhash 用于长效缓存。
filename: 'static/js/[name]/[chunkhash:8].js',
// 是输出解析文件目录,url是相对HTML页面的,这里也可以配置 `CDN` 信息
publicPath: '' // 'https://cdn.example.com/'
}
module
模块。主要是关于 rules
的配置。一般配置如下:
module: {
rules: [{
// 匹配条件,正则或字符串
test: /\.js?$/,
// 必须匹配,使用绝对路径数组
include: [path.resolve(__dirname, "app")],
// 必不匹配路径,优先级高于test和include,使用绝对路径数组
exclude: [path.resolve(__dirname, "node_modules,")],
// 对应的loader, 解析上下文
loader: "xxx-loader",
// loader可选项,query是options的别名
options: {...}
}, {
test: /\.css|\.less|\.sass|\.scss/,
// use 可以应用多个loader和选项
use: [
"style-loader",
{
loader: "sass-loader",
options: {...}
}
]
},{
// 只使用这些嵌套规则之一
oneOf: [/*rules*/]
}]
}
resolve
设置模块如何被解析。
resolve: {
// 别名,
// import xxx from '@components/xxx' === import xxx from '../src/components/xxx'
alias: {
'@components': path.resolve(__dirname, '../src/components')
},
// 自动解析确定的扩展,如文件扩展不在数组内,那需要写出来,在则可省略
extensions: ['.jsx', '.js', '.json'],
// 告诉webpack解析模块时应该搜索的目录,数组内次序是优先级的体现,前一个优先于后一个
modules: ['node_modules'],
// 译文中说的是:应该使用的额外的解析插件列表
// 我理解的意思可能是,我们有自己对模块解析的插件,将它放到这里,让webpack去解析它
plugins:[...]
}
optimization
优化配置。
optimization: {
// 开启TerserPlugin压缩bundle,区分环境,一般开发环境不会开启
minimize: true,
// 创建自定义的TerserPlugin实例
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
sourceMap: true, // Must be set to true if using source-maps in production
terserOptions: {
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
}
}),
],
// 敲黑板,划重点,这是必考点!更多信息请访问
// http://webpack.docschina.org/plugins/split-chunks-plugin/
// 这文章也说得很清晰 https://imweb.io/topic/5b66dd601402769b60847149
splitChunks: {
// async 只优化动态加载的代码,其他类型的代码正常打包。
// all 针对所有代码进行优化。
// initial 针对原始 bundle 代码进行优化。
// 三种模式详细情况请查看 https://github.com/wayou/wayou.github.io/issues/40
chunks: 'all', // 'all', 'async', 'initial',
// 表示抽取出来的文件在压缩前的最小大小,默认为 30000
minSize: 30000,
// 表示抽取出来的文件在压缩前的最大大小,默认为 0,表示不限制最大大小;
maxSize: 0,
// 表示被引用次数,默认为1;
minChunks: 1,
// 最大的按需(异步)加载次数,默认为 5;
maxAsyncRequests: 5,
// 最大的初始化加载次数,默认为 3;
maxInitialRequests: 3,
// 抽取出来的文件的自动生成名字的分割符,默认为 ~;
automaticNameDelimiter: '~',
// 抽取出来文件的名字,默认为 true,表示自动生成文件名;
name: true,
// 缓存组,配置的关键!!!它会继承/覆盖上面的参数值
cacheGroup: {
'react-vendors': {
chunks: "all",
// 要过滤的modules,可以正则匹配
test: module => {
return /react/.test(module.context);
},
name: 'react-vendors',
// 抽取权重,数字越大,优先级越高
priority: 10,
// 表示是否使用已有的 chunk
// 如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的。
// reuseExistingChunk: true
},
node_vendor: {test: /[\\/]node_modules[\\/]/, name: "vendors", chunks: "all"},
// 也能打包css,不过很少这样用,一般都是使用MiniCssExtractPlugin来抽取的
style: {
test: /\.css$/,
chunks: 'all',
enforce: true,
priority: 20
}
}
}
},
// 是否需要将模块信息清单内容(runtime)单独打包出来
// 对runtimeChunk的作用的解释 https://segmentfault.com/q/1010000014954264
// runtime内容是记录模块的信息,解析,加载,如果某个模块发生变更(如引入或删除模块,更改模块内容等)
// 将会使得runtime内容发生变化,这部分如不抽离,将会导致某个含有runtime内容的文件不能长期缓存,
// 即使该文件未发生变化。(webpack4版本以下的,需要配置CommonsChunkPlugin单独为runtime打包一个文件)
// 这是解决文件内容没发生变化,但是chunkhash却变化的问题的关键
runtimeChunk: false // false || true || 'single' || 'multiple',
// runtimeChunk值是 true || multiple 时,会为每个单独包含runtime的入口起点添加一个额外的chunk
// 该设置是以下设置的别名
// runtimeChunk: {
// name: entrypoint => `runtime~${entrypoint.name}`
// }
// runtimeChunk值是single时,会创建一个在所有生成块之间共享的运行时文件
// 该设置是以下设置的别名
// runtimeChunk: {
// name: 'runtime'
// }
}
plugins
用于以各种方式自定义 webpack 构建过程。这是一些插件。你也可以自己编写一个插件,很有趣的。小手指戳戳编写一个插件。
DefaultPlugin
能允许你创建编译时配置的全局常量。可以区分开发环境和生产环境的不同的行为。如接口请求域名开发与生产环境的区分,打印日志功能启用。具体介绍请查看DefaultPlugin
。以前曾在这里设置JQuery为常量,以解决第三方插件因访问$是undefind报错的问题。
// webpack.config.js
...
plugins: [
new webpack.DefaultPlugin({
PRODUCTION: JSON.stringify(true)
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
// 你自己编写的插件也是放在这里的哦
]
...
// index.js
if (!PRODUCTION) {
console.log('Debug info');
}
if (PRODUCTION) {
console.log('Production log');
}
// 结果
console.log('Production log');
当然,一般的第三方插件,也会写在这,比较典型的 HtmlWebpackPlugin
和 MiniCssExtractPlugin
plugins: [
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
})
]
externals
提供了「从输出的 bundle 中排除依赖」的方法,值的类型有 string
,object
,function
,regex
。
防止将某些 import
的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。
从CDN中引入JQuery而不是打包进去
// webpack.config.js
...
externals: {
jquery: 'JQuery'
}
...
// index.html
<script
src="https://code.jquery.com/jquery-3.1.0.js"
integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
crossorigin="anonymous">
</script>
或许它能提供比较有趣的功能,可以点击这里查看更详细的信息,然后去探索吧!
其他配置项,暂不介绍了,待了解并实践后再总结。
2. webpack优化打包速度
首先我们要知道,要达到目标效果,我们需要了解什么。打包慢是因为需要打包处理的东西多。那么想要加速,就必须解决这个痛点。那么怎么来解决呢?目前我了解的大概是:
其一,缓存长期不变的模块,只打包业务模块
其二,众人拾柴火焰高,一核有难,各核帮忙
其三,我自己的不要,我偏要别人的
总的来说,就是,缓存,多线程负担, 减负。
缓存:
- 有些loader可以设置缓存(如在
babel-loader
中,可以通过设置 cacheDirectory 来开启缓存) - webpack针对loader的缓存插件cache-loader
- webpack的插件hard-source-webpack-plugin
多线程负担: (假如项目不大,效果并不出众)
- 盛名于江湖而又快要隐退的 happypack
- webpack官方文档中的 thread-loader
减负:
- 引用CDN,并在webpack的externals中配置一下
- 抽离长期不变的组件,先打包成一个文件,然后往后的打包中排除这些组件,只打包业务组件,可以参考webpack编译速度提升之DllPlugin ,如果要了解
dllPlugin
,可以点击了解。
但是,在实践中,不建议在持续集成(如jenkins打包项目)中去实践,因为有可能会出现以下问题:
- 应该是更新的操作,却命中了缓存,导致打包出来的依然是上一个版本的文件;
- 缓存有可能失效了(或者没有缓存),因为jenkins打包的服务器有可能是新的没打包过的;
- 命中了缓存是n代版本的缓存,因为jenkins打包的服务器有可能会经常性转换;
- dll文件没更新(可能忘了在打包业务的时候先更新dll打包文件);
- 打包到生产环境短时间内只会打包一次,然后jenkins经历一段时间后会自动清理缓存,而且打包的服务器没有固定,以上的优化,对于经常打包的行为较友好;
- 打包的服务器不一定是2核以上的,可能多线程打包效果会更慢,因为做了一些无意义的事情。
未来会持续学习,并持续更新。
参考文章
Webpack SplitChunksPlugin 的三种模式
webpack 4 Code Splitting 的 splitChunks 配置探索
optimization.runtimeChunk 具体作用是什么?