前言
我觉得webpack的配置对于很多前端来说就是一个黑洞,首先是配置极多,其次项目大起来之后优化起来总是无从下手,后来自己通过看了众多的打包性能优化的配置之后总结下来其实就这么几点预编译,做缓存,多线程,少检索
。
在聊到性能优化前,我们先得知道怎么去分析我们打包性能。
性能分析工具
性能分析主要分为打包大小分析
和打包时间分析
。
打包大小分析
打包的大小分析的插件是webpack-bundle-analyzer
,这个插件可以将我们打包的各模块的大小可视化的展示出来了。
首先我们在package.json中写一个命令:
// package.json
"scripts": {
"analyzer": "npm run build && webpack-bundle-analyzer --port 8888 ./dist/analyzer.json"
}
webpack.prod.js代码:
// webpack.prod.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
...
plugins:{
...
new BundleAnalyzerPlugin({
analyzerMode: 'disabled',
generateStatsFile: process.env.Analyzer === 'on',
statsFilename: path.join(__dirname, '../', 'dist/analyzer.json')
})
}
效果如图所示:
当然我们得到了我们项目模块的大小详情之后,我们可以优化打出来比较大的模块,比如lodash,我们并不需要把所有的方法都打进来的,可以通过只提取使用到的方法即可。
打包时间分析
当然webpack打包我们除了要知道所有的打包出来的模块大小,还需要分析每个打包环节所花的时间,从而优化不同环节的耗时操作,对此我们要下载speed-measure-webpack-plugin
插件。
其实配置很简单,只需要在我们的webpack配置包一层即可。
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
module.exports = smp.wrap(webpackConfig) // webpackConfig指打包配置
各个环节所消耗的时间如下:
多线程
HappyPack
由于构建需要对大量文件进行解析和处理,所以构建是文件读写和计算密集型操作。当文件增多的时候,webpack构建速度会越来越慢,因为webpack是运行在Node.js的单线程模型,所以webpack在构建时只能一个一个处理任务,无法一次性处理多个任务。
让webpack支持多线程的话有两个方式:HappyPack
(不维护)和thread-loader
。
HappyPack配置:
const HappyPack = require('happypack');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module.exports = {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'happypack/loader?id=jsx'
},
],
plugins: [
new HappyPack({
id: 'jsx',
threadPool: happyThreadPool,
loaders: [
{
loader: 'babel-loader',
options: {
cacheDirectory: '.webpack_cache'
}
}
]
})
]
}
这边我们使用babel-loader
时使用指定的目录将用来缓存 loader
的执行结果。之后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程。
当然并不是所有的loader
都需要用happypack
开启多线程,因为多线程本身的启动也是需要时间的,项目不大或者不是耗时的loader
可选择不开启。
thread-loader
thread-loader配置: thread-loader配置相比于happypack会更加简单。
module.exports = {
module: {
rules: [
{
test: /\.jsx?$/,
use: ['thread-loader', 'babel-loader']
}
]
}
}
缓存
cache-loader
因为每次webpack
打包编译会把所有的文件重新打包编译一遍,这也意味着很多文件没有修改也会重新编译,实际上这样也会导致构建时间的增多,在性能开销较大的loader
,可以用cache-loader
将结果缓存下来。
module.exports = {
module: {
rules: [
{
test: /\.jsx?$/,
use: ['cache-loader', 'babel-loader']
}
]
}
}
上文我们看到其实babel-loader
自带了缓存功能,但是可能有些loader
没有这种配置,可使用cache-loader
缓存编译结果。
基础模块抽离
webpack-dll-pllugin
基础模块抽离主要就是将一些不会经常变更的第三方依赖,单独抽离出来。例如我们在项目里面常用的react全家桶,lodash
等。
实际上我们每次打包都要去编译这些几乎不需要变更的第三方依赖库,这会导致我们浪费很多时间,我们可以使用webpack-dll-plugin
库以一种预编译
的方式,将这些基础模块提前打包成一个个动态链接库
(一个链接库可包括多个模块),之后每次打包的时候就不用再去编译这些基础库,只要这些第三方依赖库的版本没有改变,我们就不需要重新去编译。
externals
除了可以使用webpack-dll-plugin
去将基础库进行预编译,还可以使用CDN
引入这些库,并配合webpack
的externals
配置不将这些库打包进去以优化构建速度。
index.html:
<script
src="https://code.jquery.com/jquery-3.1.0.js"
crossorigin="anonymous"
></script>
webpack.config.js:
module.exports = {
//...
externals: {
'jquery': 'jQuery'
}
}
这样通过import $ from 'jquery';
依然可以使用jquery。
缩小文件搜索范围
resolve.modules
由于webpack搜索第三方依赖库,先会搜索./node_modules
,然后没搜索到会继续往上一层../node_modules
,以此内推。因此我们可以指定好第三方依赖库的路径,减少搜索时间。
const path = require('path');
module.exports = {
//...
resolve: {
modules: [path.resolve(__dirname, 'node_modules')],
}
}
resolve.extensions
当我们导入的文件没有后缀的时候,我们可以通过指定resolve.extensions
来告诉webpack的后缀的搜索顺序,一般频率最高的放在最前面以此来减少搜索次数。
module.exports = {
//...
resolve: {
extensions: ['jsx','js','json']
}
}
module.noParse
由于一些如jquery
和chartjs
等库没有实现模块化标准,这样解析这些库会浪费时间而且没有意义。
module.exports = {
//...
module: {
noParse: /jquery/
}
}
loader下的exclude和include
在webpack
的loader
编译模块时,我们可以指定exclude和include
属性,exclude
表示哪些文件夹下的模块不需要编译,include
表示哪些文件夹下模块需要编译,两者同样使用的是绝对路径。
const path = require('path');
module.exports = {
//...
module: {
rules: [
{
test: /\.js[x]?$/,
use: ['babel-loader'],
exclude: [path.resolve(__dirname, 'node_modules')]
}
]
},
}
参考:
深入浅出 Webpack
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!