Webpack性能优化系列之 HMR 热模块替换
yuanyuanbyte opened this issue · 0 comments
本系列的主题是 Webpack,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末。
如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。
webpack性能优化
webpack性能优化主要指两个环境:
- 开发环境性能优化
- 生产环境性能优化
除了前面的开发环境和生产环境配置,还有以下性能优化处理:
开发环境性能优化包括:
- 优化打包构建速度
- HMR
- 优化代码调试
- source-map
生产环境性能优化包括:
- 优化打包构建速度
- oneOf
- babel缓存
- 多进程打包
- externals
- dll
- 优化代码运行的性能
- 缓存(hash-chunkhash-contenthash)
- tree shaking
- code split
- 懒加载/预加载
- pwa
HMR 热模块替换
为什么要使用HMR 热模块替换
功能?
我们回看一下前面总结的开发环境配置:
/*
开发环境配置:能让代码运行
运行项目指令:
webpack 打包运行项目
npx webpack-dev-server 启动webpack-dev-server
*/
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
// loader的配置
{
// 处理less资源
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
// 处理css资源
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
// 处理图片资源
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
// 关闭es6模块化
esModule: false,
outputPath: 'imgs'
}
},
{
// 处理html中img资源
test: /\.html$/,
loader: 'html-loader'
},
{
// 处理其他资源
exclude: /\.(html|js|css|less|jpg|png|gif)/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'media'
}
}
]
},
plugins: [
// plugins的配置
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development',
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open: true
}
};
下面我们看一下这种配置存在什么问题
运行指令:npx webpack-dev-server
,启动webpack-dev-server
然后在index.js
文件中打印一段话:console.log('index.js文件被加载了~');
只要index.js文件重新被加载,就会打印上面那段话
现在去修改样式文件
#box {
width: 400px;// 200px修改为400px
height: 200px;
background-image: url('../imgs/angular.jpg');
background-repeat: no-repeat;
background-size: 100% 100%;
}
修改之后,很明显整体页面被刷新,内容被重新打印了一次
意思就是:当我们修改css文件时,明明js文件没有变化,但js文件也被重新加载了一次,所以打包的时候,看似只修改了css文件,实际上是把js文件也重新打包了一次,这是一个问题。
再写一个模块:
在index.js中引入上面那个print模块,并且在下面调用一下:
效果:
发现模块被加载,并且模块的print方法被调用执行了
现在我们修改一下print.js文件,'hello print'
改为`'hello webpack'
console.log('print.js被加载了~');
function print() {
const content = 'hello webpack';
console.log(content);
}
export default print;
保存查看效果,可以发现整个页面被重新刷新了,所有代码重新执行:
这个意思就代表:假设以后我们有100个js模块,100个样式模块,如果只修改其中某一个模块,这整200个模块就需要重新打包,这个速度可以想想是非常慢的,更不要说以后的模块会越来越多,那打包情况只会越来越慢。
所以我们想实现这样的功能:如果只有一个模块发生变化,那么只需要修改这一个模块代码就足够了,其他模块理应是不变的
做这个功能要使用webpack的HMR 热模块替换
HMR: hot module replacement
热模块替换 / 模块热替换
作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度
HMR功能的使用非常简单,只需要在devServer里配置hot:true
即可:
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open: true,
// 开启HMR功能
// 当修改了webpack配置,新配置要想生效,必须重新webpack服务
hot: true
}
修改配置后,运行指令:npx webpack-dev-server
,重新启动webpack-dev-server
开启HMR功能跟之前更新有什么区别?
首先打印结果这里已经显示HMR功能已经开启:
下面修改样式文件,保存,可以发现只更新一个样式文件,js文件并不会重新加载:
可以发现:样式文件可以使用HMR功能,之所以能使用是因为style-loader内部实现了~
这也是为什么我们在开发环境要使用style-loader
,生产环境要用MiniCssExtractPlugin.loader
提取成单独文件,因为开发环境借助style-loader
能让性能更好,打包速度更快,但是上线的时候考虑性能优化,所以不能用
前面样式文件可以使用HMR功能,js文件行不行呢?
修改print.js
文件,保存,发现整个页面被重新刷新了一遍,这一点可以知道js文件默认不能使用HMR功能
html文件可以使用HMR功能吗?
在html文件中修改一段代码:
保存后发现当开启HMR功能后,html文件改动后并没有任何变化:
html文件: 默认不能使用HMR功能,同时会导致问题:html文件不能热更新了~
通过修改entey
入口,改成一个数组,把html文件引入就可以解决上面的问题了:
entry: ['./src/js/index.js', './src/index.html'],
修改配置后,打包运行项目
测试发现html文件也是没有HMR功能的,一旦html文件发生变化,整体页面就会重新刷新
那么html文件需要HMR功能吗?
html不像js文件,一个项目里会有很多个js模块,而运行的html只会有一个,一旦发生变化,就一定要变化,它只需要变化一个文件,既然一定要变化,那就没办法优化,所以html文件不用做HMR功能
前面讲到了js默认不能使用HMR功能,这并不代表js就不需要HMR功能,如何对js文件使用HMR功能?
用法:
if (module.hot) {
// 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效
module.hot.accept('./print.js', function() {
// 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。
// 会执行后面的回调函数
print();
});
}
修改print.js
,查看效果
可以发现,print.js
会重新更新,但index.js
不会变,即index.js
不会重新加载,样式和结构也不会重新加载,这就是js文件的HMR功能的使用
注意:HMR功能对js的处理,只能处理非入口js文件的其他文件。
为什么呢?
因为入口文件会将其他文件全部引入,一旦入口文件变化,其他文件重新引入,就会重新加载,这是没办法阻止的,所以入口文件是做不了HMR功能的,只能对入口文件引入的一些依赖或者其他文件做HMR功能。
比如修改入口文件下面的打印代码:
保存,可以发现所有东西都要重新打包构建:
参考
博文系列目录
- JavaScript 深入系列
- JavaScript 专题系列
- JavaScript 基础系列
- 网络系列
- 浏览器系列
- Webpack 系列
- Vue 系列
- 性能优化与网络安全系列
- HTML 应知应会系列
- CSS 应知应会系列
交流
各系列文章汇总:https://github.com/yuanyuanbyte/Blog
我是圆圆,一名深耕于前端开发的攻城狮。