Webpack优化指南
Webpack的优化主要包括以下的目的:
优化开发体验:
优化构建速度。 在项目庞大时构建耗时可能会变的很长,每次等待构建的耗时加起来也会是个大数目。
优化使用体验。 通过自动化手段完成一些重复的工作,让我们专注于解决问题本身。
优化输出质量:
优化输出质量的目的是为了给用户呈现体验更好的网页,例如减少首屏加载时间、提升性能流畅度等。 这至关重要,因为在互联网行业竞争日益激烈的今天,这可能关系到你的产品的生死。
优化输出质量本质是优化构建输出的要发布到线上的代码,分为以下几点:
减少用户能感知到的加载时间,也就是首屏加载时间。
提升流畅度,也就是提升代码性能。
缩小文件搜索范围
优化loader配置
由于 Loader 对文件的转换操作很耗时,需要让尽可能少的文件被 Loader 处理。可以通过 test 、 include 、 exclude 三个配置项来命中 Loader 要应用规则的文件。
module.exports = {
module: {
rules: [
{
// 如果项目源码中只有 js 文件就不要写成 /\.jsx?$/,提升正则表达式性能
test: /\.js$/,
// babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启
use: ['babel-loader?cacheDirectory'],
// 只对项目根目录下的 src 目录中的文件采用 babel-loader
include: path.resolve(__dirname, 'src'),
},
]
},
};
优化 resolve
resolve.modules 的默认值是 ['node_modules'],含义是先去当前目录下的 ./node_modules 目录下去找想找的模块,如果没找到就去上一级目录 ../node_modules 中找,再没有就去 ../../node_modules 中找,以此类推。
当安装的第三方模块都放在项目根目录下的 ./node_modules 目录下时,没有必要按照默认的方式去一层层的寻找,可以指明存放第三方模块的绝对路径,以减少寻找,配置如下:
module.exports = {
resolve: {
// 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
// 其中 __dirname 表示当前工作目录,也就是项目根目录
modules: [path.resolve(__dirname, 'node_modules')]
},
};
在实战项目中经常会依赖一些庞大的第三方模块,以React为例,通过配置 resolve.alias 可以让 Webpack 在处理 React 库时,直接使用单独完整的 react.min.js 文件,从而跳过耗时的递归解析操作。
module.exports = {
resolve: {
// 使用 alias 把导入 react 的语句换成直接使用单独完整的 react.min.js 文件,
// 减少耗时的递归解析操作
alias: {
'react': path.resolve(__dirname, './node_modules/react/dist/react.min.js')
}
},
};
在导入语句没带文件后缀时,Webpack 会自动带上后缀后去尝试询问文件是否存在。如果这个列表越长,或者正确的后缀在越后面,就会造成尝试的次数越多,所以 resolve.extensions 的配置也会影响到构建的性能。 在配置 resolve.extensions 时你需要遵守以下几点,以做到尽可能的优化构建性能:
后缀尝试列表要尽可能的小,不要把项目中不可能存在的情况写到后缀尝试列表中。
频率出现最高的文件后缀要优先放在最前面,以做到尽快的退出寻找过程。
在源码中写导入语句时,要尽可能的带上后缀,从而可以避免寻找过程。例如在你确定的情况下把 require('./data') 写成 require('./data.json')。
优化 module.noParse 配置
module.noParse 配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析处理,这样做的好处是能提高构建性能。
在上面的 优化 resolve.alias 配置 中讲到单独完整的 react.min.js 文件就没有采用模块化,让我们来通过配置 module.noParse 忽略对 react.min.js 文件的递归解析处理, 相关 Webpack 配置如下:
const path = require('path');
module.exports = {
module: {
// 独完整的 `react.min.js` 文件就没有采用模块化,忽略对 `react.min.js` 文件的递归解析处理
noParse: [/react\.min\.js$/],
},
};
使用Dlls
使用DllPlugin插件将不常变更的代码进行单独编译。尽管这会增加构建的复杂度,但会提高应用的编译速度。
使用DllPlugin配置一个webpack_dll.config.js来构建dll文件:
const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
module.exports = {
entry:{
react:['react','react-dom'],
polyfill:['core-js/fn/promise','whatwg-fetch']
},
output:{
filename:'[name].dll.js',
path:path.resolve(__dirname, 'dist'),
library:'_dll_[name]', //dll的全局变量名
},
plugins:[
new DllPlugin({
name:'_dll_[name]', //dll的全局变量名
path:path.join(__dirname,'dist','[name].manifest.json'),//描述生成的manifest文件
})
]
}
在主config文件里使用DllReferencePlugin插件引入xx.manifest.json文件:
const path = require('path');
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
module.exports = {
entry:{ main:'./main.js' },
//... 省略output、loader等的配置
plugins:[
new DllReferencePlugin({
manifest:require('./dist/react.manifest.json')
}),
new DllReferenctPlugin({
manifest:require('./dist/polyfill.manifest.json')
})
]
}
多线程构建
核心原理:使用 thread-loader 将 loader 放置在一个 worker 池里面运行,以达到多线程构建,减少构建时间。
module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: path.resolve("src"),
use: [
"thread-loader",
// 你的高开销的loader放置在此 (e.g babel-loader)
]
}
]
}
每个 worker 都是一个单独的有 600ms 限制的 node.js 进程。同时跨进程的数据交换也会被限制。请在高开销的loader中使用,否则效果不佳
压缩代码
在 Webpack3 中,我们一般使用 UglifyJS 来压缩代码,但是这个是单线程运行的,为了加快效率,我们可以使用 webpack-parallel-uglify-plugin 来并行运行 UglifyJS,从而提高效率。在 Webpack4 中,我们就不需要以上这些操作了,只需要将 mode 设置为 production 就可以默认开启以上功能。代码压缩也是我们必做的性能优化方案,当然我们不止可以压缩 JS 代码,还可以压缩 HTML、CSS 代码,并且在压缩 JS 代码的过程中,我们还可以通过配置实现比如删除 console.log 这类代码的功能。
启用Tree Shaking
修改.babelrc以保留ES6模块化语句
启动webpack时带上 --display-used-exports可以在shell打印出关于代码剔除的提示
使用UglifyJSPlugin,或者启动时使用--optimize-minimize
在使用第三方库时,需要配置 resolve.mainFields: ['jsnext:main', 'main'] 以指明解析第三方库代码时,采用ES6模块化的代码入口
- 点赞
- 收藏
- 关注作者
评论(0)