Webpack 实现多模块架构场景下的 css 和 js 的增量更新构建
一、场景说明
webpack 构建的项目中,存在多个模块,这里举例存在两个模块,分别是 main.js
和 app.js
。
正常构建过程中,我们会构建出 main.js 和 app.js 两个 filename 的文件,一旦项目更新,模块的缓存更新会存在问题,所以一般我们会采用 hash
的方式,然而在多模块构建的场景下,hash
并不能满足这种场景的需求。
比如项目中有 10+ 模块,如果通过 hash 构建,所有的模块的 hash 都会发生变化,所有的 hash 都需要更新,这并不符合最小更新原则。
示例项目中有两个入口,分别是 app.js 和 main.js,文件目录如下:
1、html template
通过插件 html-webpack-plugin
生成默认的 html 模板,留了两个 id 的口子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<div id="time"></div>
</body>
</html>
2、main.js
main.js 用于向 #app 注入标题
var app = document.querySelector('#app');
app.innerHTML = '<h1> APP </h1>'
3、app.js
app.js 用于向 #time 动态注入时间
var time = document.querySelector('#time');
setInterval(function () {
time.innerHTML = '<h3> ' + Date.now() + ' </h3>';
}, 1000);
二、hash 构建的场景
如果 模块 output 的 filename 通过 hash 构建,则在输出过程中,所有的模块会被打上同一个 hash。
一个简单的 hash 构建示例:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: {
'main': path.resolve(__dirname, './src/main.js'),
'app': path.resolve(__dirname, './src/app.js'),
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[hash:8].js'
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './public/index.html')
}),
],
}
构建结果:
可以发现两个 js 文件的 hash 是完全一样的
如果我改动了 app.js 然后再次构建:
两者的 hash 还是一样,这就意味着,虽然我只改动了 app.js 但是项目浏览器更新的时候,无论是浏览器还是 CDN 都需要更新 main.js ,这显然不符合增量更新的需求。
三、chunkhash 构建
从上面可以看出,hash 并不适合增量更新的构建场景。
chunk 在 webpack 中表示的是散列模块经过合并后的 块
,比如上面 main.js 和 app.js 都是不同的 chunk
使用 chunkhash 构建的配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: {
'main': path.resolve(__dirname, './src/main.js'),
'app': path.resolve(__dirname, './src/app.js'),
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[chunkhash:8].js'
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './public/index.html')
}),
],
}
第一次构建结果,可以发现 app 和 main 是两个 chunk,因此 hash 也是不同的
当修改了 app.js,重新构建:
可以发现,只有 app.js 的 hash 变化了,而 main.js 是没有任何变化的,因此 chunkhash 是可以满足增量更新的输出的。
四、contenthash 构建
上面只是对 js chunk 进行了处理,实现了增量更新的构建场景,但是一旦涉及到 css 的增量更新会出现问题。
1、加入 css 的项目
改造一下上面的项目,创建了两个 css 文件,并且都在 app.js 中引入,其中项目目录如下:
app.css 内容:
body{
display: flex;
justify-content: center;
align-items: center;
}
#div {
color:#FF5000;
}
#time {
font-size: 36px;
}
app2.css 内容:
#time {
color: red;
}
app.js 文件内容:
require('./css/app.css');
require('./css/app2.css');
import querystring from 'querystring';
var time = document.querySelector('#time');
setInterval(function () {
time.innerHTML = '<h3> ' + Date.now() + ' </h3>';
}, 1000);
2、通过 mini-css-extract-plugin chunkhash 分离 css 文件
webpack 配置如下,其中对于 css 文件,通过 nini-css-extract-plugin 处理,注意 webpack V4 之后,不能通过 extract-text-plugin 处理了
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'development',
entry: {
'main': path.resolve(__dirname, './src/main.js'),
'app': path.resolve(__dirname, './src/app.js'),
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[chunkhash:8].js'
},
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
// you can specify a publicPath here
publicPath: '../assets/',
hmr: process.env.NODE_ENV === 'development',
},
},
'css-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './public/index.html')
}),
new MiniCssExtractPlugin({
filename: '[name].[chunkhash:8].css',
chunkFilename: '[id].css',
}),
],
}
可以发现,在插件的配置中,filename
我是用了 chunkhash:8
作为 chunk:
new MiniCssExtractPlugin({
filename: '[name].[chunkhash:8].css',
chunkFilename: '[id].css',
}),
因此构建结果如下:
可以发现的是,app.css 和 app.js 的 hash 是一样的, 这就意味着,如果我只变动了 app.css 或者 app2.css,没有变动 app.js ,我的 app.js 的文件名也是发生了变动
下面是修改了 app2.css 然后构建的结果:
可以发现,app.css 和 app.js 全部都发生了变化,这显然也不符合我们的要求
3、通过 mini-css-extract-plugin contenthash 分离 css
变动不多,只是将插件的配置和 output 中 filename 变化了一下:
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash:8].js'
},
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css',
chunkFilename: '[id].css',
}),
第一次构建结果如下:
只修改 app.css ,然后进行构建,第二次构建结果如下:
五、完整代码示例
github 地址:
https://github.com/postbird/wenpack-incremental-update-build-demo
文章版权:Postbird-There I am , in the world more exciting!
本文链接:http://www.ptbird.cn/webpack-hash-chunkhash.html
转载请注明文章原始出处 !