webpack基本知识咱就不多说了。关于webpack优化,网上有很多文章,介绍了各种各样的优化方法。我随机挑了几篇看了看,大部分文章优化围绕的中心思想都是如何提升编译速度。但右三觉得,webpack优化不仅仅是要让webpack编译越来越快,而应该包含以下3个目标:
如果你的项目有前端构建,随着项目越来越大,“编译慢”迟早会成为让人头疼的问题。想想这个场景,你修改代码,准备看页面效果,但却要等上5s甚至更长时间。前端开发的同学大多是多显示器的,一边写代码,一边即时预览效果。但因为编译慢的原因,这种无缝衔接的工作方式被打破了。这不但影响工作效率,更影响心情,工作被打断,等等等的滋味可不好受。这也是我们追求更快编译速度的根本原因。
要提升编译速度,在项目大小,机器确定的前提下,大概的优化方向也是3个(grunt/gulp同理):
我们一起来看看,在这3个方向上,如何提升webpack的编译速度
webpack watch 默认就是增量编译,我们就不需要再额外做什么了
module.exports = {
cache: true,
// ... 其他配置
}
module: {
rules: [
{
test: /\.js$/,
loader: ['babel-loader?cacheDirectory=true'] // 开启babel-loader缓存
}
]
}
hard-source-webpack-plugin这个插件对开发时编译速度的提升是巨大的,比开启其它缓存(webpack缓存或loader缓存)效果好太多。我手上一个项目,没优化前watch编译一次大约要5s,加上这个插件后,直接降到2s左右,强烈推荐这个插件
happypack插件为webpack增加并行处理的能力。也强烈建议使用,如果项目基于vue要注意vue-loader目前还不支持happypack
除了上面列到的方法,优化webpack模块查询,从而减少webpack自身执行开销,对提升编译速度也有一定帮助。具体做法是:
每个项目都会有一些基础依赖库,如果能把这些依赖提前编译好,这就减少了webpack要处理的模块,自然,也是可以提升编译速度的。webpack.DllPlugin可以把依赖打包成dll库,供其它模块使用。
let config = {
resolve: {
extensions: ['.js', '.vue'],
alias: {
'@@': path.resolve(__dirname, '../node_modules'),
}
},
entry: {
// 把vue全家桶,axios等打包成dll库
webDll: [
'@@/vue/dist/vue.esm.js',
'@@/vue-router',
'@@/vuex',
'@@/axios',
'@@/urijs'
]
}
,
output: {
path: path.join(__dirname, dir),
filename: '[name].js',
library: '[name]_[hash]'
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, dir, '[name]-manifest.json'),
name: '[name]_[hash]',
context: __dirname
}),
],
module: baseConfig.module
}
// 引用 dll-manifest.json文件
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require(`./${manifestDir}/webDll-manifest.json`)
})
指定不需要解析的模块
排除外部依赖,不需要对其进行处理
到目前,上面的内容主要是围绕开发(development)场景进行的,方便我们高效无缝地进行开发。但相比开发环境,发布到线上的前端资源就更重要了。因为这才是项目真正使用的资源文件,所以对生产环境(production)的前端资源文件进行webpack优化也是必不可少的。
在生产环境,我们的优化原则就是文件越小越好,越少越好。一般来说,页面需要加载的文件体积越小,加载的文件个数越少,页面的性能就越可能更好(这里的页面性能主要指首屏时间和页面可交互时间。这里为什么加“可能”两个字,可以看第三部分);同时,更小更少的文件,就意味着更小更少的带宽和流量。CDN可是要花钱的,这也是直接帮公司省钱呢。
那么如何让我们的文件能越来越小呢?答案很简单:同一个模块只打包一份,文件大小就是最小的。这时相信大家都想到了webpack.optimize.CommonsChunkPlugin插件,也都会在webpack中加上类似如下的配置:
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: 2
})
CommonsChunkPlugin插件会帮我们把重复的模块提取到vendor这个chunk中。但如果大家仔细研究过webpack最终打包的dist文件,就会发现,有一些重复引用多次的文件并没有被提取到vendor中,而是在多个chunk中被重复打包了。这里推荐使用webpack官方分析平台,上传stats.json后,在hints里面会有一些打包建议,基本上都是重复打包的信息。我们的目标就是要把hints的建议全解决了。
到于如何得到stats.json文件,可以直接使用命令参数 --json,也可以通过代码的方式生成:
var webpack = require('webpack/lib/webpack.js')
var options = require('./build/webpack.conf')
var compiler = webpack(options)
compiler.run(function (err, stats) {
if (err) throw err
// 生成stats.json文件
fs.writeFile(path.resolve(__dirname, 'webpack.stats.json'), JSON.stringify(stats.toJson(), null, ' '), (err) => {
if (err) throw err
})
})
那么为什么有些多次重复引用的模块没有被提到vendor中呢。原因是不同chunk间的多次引用不算重复引用 System.import, require.ensure等方法会生成新的chunk,这些chunk间多次重复引用同一模块,并不会被提到vendor中。在下面例子中,index.js,detail.js,about.js都依赖了模块m.js,但m.js会在每个chunk(System.import)中都存在,并不是我们相像的那样,被单独提取出来了:
// main.js
import Index from './index'
import Detail from './detail'
import About from './about'
const Index = System.import('./index')
const Detail = System.import('./detail')
const About = System.import('./about')
要想m.js被单独出来,可以使用require.include,把指定模块加入到特定chunk中。下面例子中的m.js就会在main.js所在的chunk中了,而不会在重复存在于index,detail,about这3个chunk:
// main.js
import Index from './index'
import Detail from './detail'
import About from './about'
require.include('./m')
const Index = System.import('./index')
const Detail = System.import('./detail')
const About = System.import('./about')
虽然require.include可以在多个异步chunk中解决依赖模块被重复打包的问题,但在实际使用上,还是有一些不足。如果公共依赖模块m.js很多,这个时候到入口chunk中去一个一个加require.include显然不现实;更重要的是,到入口chunk中去加require.include这本身就不灵活,因为打包入口chunk是可以任意指定的。
webpack当然考虑到了这个问题。解决办法就是使用webpack.optimize.CommonsChunkPlugin的异步公共模块:
new webpack.optimize.CommonsChunkPlugin({
async: true,
minChunks: function (module, count) {
// 强制指定把m.js打包成一个异步依赖模块
return module.resource && module.resource.indexOf('m.js') > -1
},
})
通过上面的配置,webpack也会把异步chunk中(各chunk中虽然只引用了一次m.js)的m.js独立打包成一个异步依赖,在加载主chunk时会先加载m.js依赖。这样也保证了m.js在打包时只打包了一份。
webpack帮助我们打包生成对应的dist文件,那么怎样生成的dist文件是最有助于提升页面性能呢?web性能优化原则中与文件相关的规则有以下一些:
所以,如何结合业务自身情况,合理地配置chunk进行代码分片,从而充分利用缓存,更合理地做预加载,按需加载等,这些都是可以结合webpack,不断调优的。
webpack现在已经成为前端构建标配,本文主要围绕提高编译速度、减小编译输出的文件体积、帮助提升页面性能三个方面阐述了右三对webpack优化的理解,希望对读者有些许帮助。
发表评论: