webpack 性能优化
- 开发环境性能优化
- 生产环境性能优化
开发环境性能优化
- 优化打包构建速度
HMR(热模块替换)
当我们修改某一个文件,但其它的文件都没有修改时,用 webpack 进行打包,会发现所有的 文件都会被重新打包。但是只修改某一个文件,却将所有的文件都打包,这样做反而是没有必要的。所以,我们就要做的是,那个文件进行了修改,再打包的时候只打印这个文件即可,所以我们就需要使用到 HMR(热模块替换)
常见的文件有三种:html、js 和 css
- css文件,可以使用 HMR,因为 style-loader 内部实现了
- js 文件,默认不使用 HMR,需要修改入口文件来监听其它 js 文件的变化,另外入口文件没有 HMR 功能,所以当入口文件改变时,所有的文件都会被重新编译
- html 文件:当开启 HMR 功能后,html 文件改变不会被重新进行重新编译,需要在 entry 中加入 html 文件
if (module.hot) {
// 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效
module.hot.accept('./print.js', function() {
// 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。
// 会执行后面的回调函数
// 要监听改变的代码
});
}
source-map
一种提供源代码到构建后代码映射技术,如果构建后代码出错了,通过映射可以追踪源代码错误的语句
生成位置 | 是否有错误代码的原因 | 是否有错误的位置 | |
---|---|---|---|
source-map | 外部 | 有 | 有 |
hidden-source-map | 外部 | 有 | 不能准确追踪源代码错误位置,只提示构建后代码的错误位置 |
nosources-source-map | 外部 | 有 | 没有 |
cheap-source-map | 外部 | 有 | 源代码的错误位置,精确到行,提示该行全部出错 |
cheap-module-source-map | 外部 | 有 | 有,nodule会将 loader 的 sourec-map 加入 |
inline-source-map | 内联 | 有 | 有 |
eval-source-map | 内联 | 有 | 有 |
生成文件内联和外部的区别
- 外部生成单独的文件,内联没有
- 内联构建速度更快
开发环境建议用:eval-source-map / eval-cheap-module-source-map
生产环境建议用 source-map / cheap-module-source-map
如果要隐藏代码可以用 nosources-source-map / hidden-source-map
nosources-source-map 的 hidden-source-map 区别:一个是半隐藏一个是全隐藏
oneOf
若多个配置有用到相同的loader,则只加载一个,减少资源消耗
但是oneOf中不能有两个配置处理同一种类型文件
const {
resolve
} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: ['./src/index.js', './src/index.html'],
output: {
filename: './js/index.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
// 比如这里可以配置 eslint
},
{
oneOf: [{
test: /\.(css|less)$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[name][hash:10].[ext]',
outputPath: 'imgs'
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
exclude: /\.(html|css|less|js|png|jpg|svg|gif|png|jpg)$/,
loader: 'file-loader',
options: {
name: '[hash:10][ext]',
outputPath: 'static'
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development',
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
open: true,
port: 4000,
hot: true,
devtool: 'source-map'
}
}
缓存
当某些文件已经加载后,再次加载时,不会重新加载,而是从缓存中读取已经加载过的文件
- babel缓存 文件再次编译的时候会优先从缓存中取
- 文件资源缓存
- hash: 每次webpack构建时会生成一个唯一的hash值。问题:js和css会同时使用同一个hash值,当只改变一个文件时,重新打包会使缓存失效
- chunkhash:根据chunk生成的hash值,如果打包来源于同一个chunk,那么hash值就一样。问题:js和css的值还是一样的,因为css是在js中被引入的,所以同属于一个chunk
- contenthash:根据文件内容生成hash值,不同文件hash值一定不一样
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.[contenthash:10].js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
// eslint
},
{
oneOf: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: { version: 3 },
targets: {
chrome: '60',
firefox: '50'
}
}
]
],
// 开启babel缓存
// 第二次构建时,会读取之前的缓存
cacheDirectory: true
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/built.[contenthash:10].css'
}),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
],
mode: 'production',
devtool: 'source-map'
};
tree shaking
tree shaking: 去除无用代码
- 使用es6模块化 2.开启production环境
- package.json中的配置
- “sideEffects”: false 所有代码没有副作用
- 问题:可能会把css等直接引入但在js文件中没有使用的文件给抹去
- “sideEffects”: ["*.css"] //css不被优化