举个很常见的例子,比如你在做一个数据可视化类型的网站,引用到了百度的Echarts作为第三方库来渲染图表,如果你将自己的代码和Echarts打包在一起生成一个main.bundle.js文件,这样的结果就是在一个网速欠佳的环境下打开你的网站时,用户可能需要面对很长时间的白屏,你很快就会想到将Echarts从主文件中剥离出来,让体积较小的主文件先在界面上渲染出一些动画或是提示信息,然后再去加载Echarts,而分离出的Echarts也可以从速度更快的CDN节点获取,如果加载某个体积庞大的库,你也可以选择使用懒加载的方案,将脚本的下载时机延迟到用户真正使用对应的功能之前。这就是一种人工的代码分割。
从上面的例子整个的生命周期来看,我们将原本一次就可以加载完的脚本拆分为了两次,这无疑会加重服务端的性能开销,毕竟建立TCP连接是一种开销很大的操作,但这样做却可以换来对渲染节奏的控制和用户体验的提升,异步模块和懒加载模块从宏观上来讲实际上都属于代码分割的范畴。code splitting最极端的状况其实就是拆分成打包前的原貌,也就是源码直接上线。
3. 代码分割的本质
代码分割的本质,就是在“源码直接上线”和“打包为唯一的脚本main.bundle.js”这两种极端方案之间寻找一种更符合实际场景的中间状态,用可接受的服务器性能压力增加来换取更好的用户体验。
4. 配置代码分割
code-splitting技术的配置和使用方法将在下一小节详细描述。
5. 更细致的代码分割
感兴趣的读者可以参考来自google开发者社区的文章自行研究。
3.5 代码混淆压缩webpack4中已经内置了UglifyJs插件,当打包模式参数mode设置为production时就会自动开启,当然这不是唯一的选择,babel的插件中也能提供代码压缩的处理,具体的效果和原理笔者尚未深究,感兴趣的读者可以自行研究。
四. 细说splitChunks技术 4.1 参数说明webpack4废弃了CommonsChunkPlugin插件,使用optimization.splitChunks和optimization.runtimeChunk来代替,原因可以参考《webpack4:连奏中的进化》一文。关于runtimeChunk参数,有的文章说是提取出入口chunk中的runtime部分,形成一个单独的文件,由于这部分不常变化,可以利用缓存。google开发者社区的博文是这样描述的:
is also specified to move into the vendors chunk to avoid duplication of it in our app code.
splitChunks中默认的代码自动分割要求是下面这样的:
node_modules中的模块或其他被重复引用的模块
就是说如果引用的模块来自node_modules,那么只要它被引用,那么满足其他条件时就可以进行自动分割。否则该模块需要被重复引用才继续判断其他条件。(对应的就是下文配置选项中的minChunks为1或2的场景)
分离前模块最小体积下限(默认30k,可修改)
30k是官方给出的默认数值,它是可以修改的,上一节中已经讲过,每一次分包对应的都是服务端的性能开销的增加,所以必须要考虑分包的性价比。
对于异步模块,生成的公共模块文件不能超出5个(可修改)
触发了懒加载模块的下载时,并发请求不能超过5个,对于稍微了解过服务端技术的开发者来说,【高并发】和【压力测试】这样的关键词应该不会陌生。
对于入口模块,抽离出的公共模块文件不能超出3个(可修改)
也就是说一个入口文件的最大并行请求默认不得超过3个,原因同上。
4.2 参数配置splitChunks的在webpack4.0以上版本中的用法是下面这样的:
module.exports = { //... optimization: { splitChunks: { chunks: 'async',//默认只作用于异步模块,为`all`时对所有模块生效,`initial`对同步模块有效 minSize: 30000,//合并前模块文件的体积 minChunks: 1,//最少被引用次数 maxAsyncRequests: 5, maxInitialRequests: 3, automaticNameDelimiter: '~',//自动命名连接符 cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, minChunks:1,//敲黑板 priority: -10//优先级更高 }, default: { test: /[\\/]src[\\/]js[\\/]/ minChunks: 2,//一般为非第三方公共模块 priority: -20, reuseExistingChunk: true } }, runtimeChunk:{ name:'manifest' } } } 4.3 代码分割实例注:实例中使用的demo及配置文件已放在附件中。
单页面应用