webpack4.0各个击破(4)—— Javascript & splitChunk
目录
webpack
作为前端最火的构建工具,是前端自动化工具链最重要的部分,使用门槛较高。本系列是笔者自己的学习记录,比较基础,希望通过问题 + 解决方式的模式,以前端构建中遇到的具体需求为出发点,学习webpack
工具中相应的处理办法。(本篇中的参数配置及使用方式均基于webpack4.0版本
)本篇摘要:
本篇主要介绍基于
webpack4.0
的splitChunks
分包技术。
一. Js模块化开发
javascript
之所以需要打包合并,是因为模块化开发的存在。开发阶段我们需要将js
文件分开写在很多零碎的文件中,方便调试和修改,但如果就这样上线,那首页的http
请求数量将直接爆炸。同一个项目,别人2-3个请求就拿到了需要的文件,而你的可能需要20-30个,结果就不用多说了。
但是合并脚本可不是“把所有的碎片文件都拷贝到一个js
文件里”这样就能解决的,不仅要解决命名空间冲突的问题,还需要兼容不同的模块化方案,更别提根据模块之间复杂的依赖关系来手动确定模块的加载顺序了,所以利用自动化工具来将开发阶段的js
脚本碎片进行合并和优化是非常有必要的。
二. Js文件的一般打包需求
- 代码编译(
TS
或ES6
代码的编译) - 脚本合并
- 公共模块识别
- 代码分割
- 代码压缩混淆
三. 使用webpack处理js文件
3.1 使用babel转换ES6+语法
babel
是ES6
语法的转换工具,对babel
不了解的读者可以先阅读《大前端的自动化工厂(3)——Babel》一文进行了解,babel
与webpack
结合使用的方法也在其中做了介绍,此处仅提供基本配置:
webpack.config.js
:
... module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: [ { loader: 'babel-loader' } ] } ] }, ...
.babelrc
:
{ "presets":[ ["env",{ "targets":{ "browsers":"last 2 versions" } } ]], "plugins": [ "babel-plugin-transform-runtime" ] }
3.2 脚本合并
使用webpack
对脚本进行合并是非常方便的,毕竟模块管理和文件合并这两个功能是webpack
最初设计的主要用途,直到涉及到分包和懒加载的话题时才会变得复杂。webpack
使用起来很方便,是因为实现了对各种不同模块规范的兼容处理,对前端开发者来说,理解这种兼容性实现的方式比学习如何配置webpack
更为重要。webpack
默认支持的是CommonJs
规范,但同时为了扩展其使用场景,webpack
在后续的版本迭代中也加入了对ES harmony
等其他规范定义模块的兼容处理,具体的处理方式将在下一章《webpack4.0各个击破(5)—— Module篇》详细分析。
3.3 公共模块识别
webpack
的输出的文件中可以看到如下的部分:
/******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ }
上面的__webpack_require__( )
方法就是webpack
的模块加载器,很容易看出其中对于已加载的模块是有统一的installedModules
对象来管理的,这样就避免了模块重复加载的问题。而公共模块一般也需要从bundle.js
文件中提取出来,这涉及到下一节的“代码分割”的内容。
3.4 代码分割
1. 为什么要进行代码分割?
代码分割最基本的任务是分离出第三方依赖库,因为第三方库的内容可能很久都不会变动,所以用来标记变化的摘要哈希contentHash
也很久不变,这也就意味着我们可以利用本地缓存来避免没有必要的重复打包,并利用浏览器缓存避免冗余的客户端加载。另外当项目发布新版本时,如果第三方依赖的contentHash
没有变化,就可以使用客户端原来的缓存文件(通用的做法一般是给静态资源请求设置一个很大的max-age
),提升访问速度。另外一些场景中,代码分割也可以提供对脚本在整个加载周期内的加载时机的控制能力。
2. 代码分割的使用场景
举个很常见的例子,比如你在做一个数据可视化类型的网站,引用到了百度的Echarts
作为第三方库来渲染图表,如果你将自己的代码和Echarts
打包在一起生成一个main.bundle.js
文件,这样的结果就是在一个网速欠佳的环境下打开你的网站时,用户可能需要面对很长时间的白屏,你很快就会想到将Echarts
从主文件中剥离出来,让体积较小的主文件先在界面上渲染出一些动画或是提示信息,然后再去加载Echarts
,而分离出的Echarts
也可以从速度更快的CDN
节点获取,如果加载某个体积庞大的库,你也可以选择使用懒加载的方案,将脚本的下载时机延迟到用户真正使用对应的功能之前。这就是一种人工的代码分割。
从上面的例子整个的生命周期来看,我们将原本一次就可以加载完的脚本拆分为了两次,这无疑会加重服务端的性能开销,毕竟建立TCP连接是一种开销很大的操作,但这样做却可以换来对渲染节奏的控制和用户体验的提升,异步模块和懒加载模块从宏观上来讲实际上都属于代码分割的范畴。code splitting
最极端的状况其实就是拆分成打包前的原貌,也就是源码直接上线。
3. 代码分割的本质
代码分割的本质,就是在“源码直接上线”和“打包为唯一的脚本main.bundle.js”这两种极端方案之间寻找一种更符合实际场景的中间状态,用可接受的服务器性能压力增加来换取更好的用户体验。
4. 配置代码分割
code-splitting
技术的配置和使用方法将在下一小节详细描述。
5. 更细致的代码分割
感兴趣的读者可以参考来自google开发者社区的文章《Reduce JavaScript Payloads with Code Splitting》自行研究。
3.5 代码混淆压缩
webpack4
中已经内置了UglifyJs
插件,当打包模式参数mode
设置为production
时就会自动开启,当然这不是唯一的选择,babel
的插件中也能提供代码压缩的处理,具体的效果和原理笔者尚未深究,感兴趣的读者可以自行研究。
四. 细说splitChunks技术
4.1 参数说明
webpack4
废弃了CommonsChunkPlugin
插件,使用optimization.splitChunks
和optimization.runtimeChunk
来代替,原因可以参考《webpack4:连奏中的进化》一文。关于runtimeChunk
参数,有的文章说是提取出入口chunk中的runtime部分,形成一个单独的文件,由于这部分不常变化,可以利用缓存。google开发者社区的博文是这样描述的:
The
runtimeChunk
option is also specified to move webpack's runtime into thevendors
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
的在webpack
4.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及配置文件已放在附件中。
-
单页面应用
单页面应用只有一个入口文件,
splitChunks
的主要作用是将引用的第三方库拆分出来。从下面的分包结果就可以看出,node_modules
中的第三方引用被分离了出来,放在了vendors-main.[hash].js
中。
-
多页面应用
多页面应用的情形稍显复杂,以《webpack4:连奏中的进化》一文中的例子进行代码分割处理,源码的依赖关系为:
entryA.js: vue vuex component10k entryB.js: vue axios component10k entryC.js: vue vuex axios component10k
经过代码分割后得到的包如下图所示:
splitChunks
提供了更精确的分割策略,但是似乎无法直接通过html-webpack-plugin
配置参数来动态解决分割后代码的注入问题,因为分包名称是不确定的。这个场景在使用chunks:'async'
默认配置时是不存在的,因为异步模块的引用代码是不需要以<script>
标签的形式注入html
文件的。
当chunks
配置项设置为all
或initial
时,就会有问题,例如上面示例中,通过在html-webpack-plugin
中配置excludeChunks
可以去除page和about这两个chunk,但是却无法提前排除vendors-about-page这个chunk,因为打包前无法知道是否会生成这样一个chunk。这个场景笔者并没有找到现成的解决方案,对此场景有需求的读者也许可以通过使用html-webpack-plugin
的事件扩展来处理此类场景,也可以使用折中方案,就是第一次打包后记录下新生成的chunk名称,按需填写至html-webpack-plugin
的chunks
配置项里。
### 4.4 结果分析
通过Bundle Buddy
分析工具或webpack-bundle-analyser
插件就可以看到分包前后对于公共代码的抽取带来的影响(图片来自参考文献的博文):
五. 参考及附件说明
【1】附加中文件说明:
-
webpack.spa.config.js
——单页面应用代码分割配置实例 -
main.js
——单页面应用入口文件 -
webpack.multi.config.js
——多页面应用代码分割配置实例 -
entryA.js
,entryB.js
,entryC.js
——多页面应用的3个入口

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
【译Py】2018年8月,GitHub上的Python数据科学明星项目:自动化机器学习、自然语言处理、可视化、机器学习工作流
原文作者:Matthew Mayo 原文地址:GitHub Python Data Science Spotlight: AutoML, NLP, Visualization, ML Workflows Python数据分析 本文是“五个不容忽视的机器学习项目”一文的续篇。和上篇文章相比,这次选出的项目涉及更多数据科学领域,并且都是GitHub上的开源项目,我们为每个项目都附上了Repo、文档和入门指南的链接,并对每个项目进行了简单介绍。 下面一起来了解一下这些新兴的热门Python库吧,希望本文对你的工作能有所帮助: Auto-Keras自动机器学习库 项目链接:https://github.com/jhfjhfj1/autokeras 文档:http://autokeras.com 入门指南:https://autokeras.com/#example Auto-Keras是用于自动机器学习(AutoML)的开源软件库。自动机器学习的最终目标是让仅拥有一定数据科学知识或机器学习背景的行业专家可以轻松地应用深度学习模型。Auto-Keras提供了很多用于自动研究深度学习模型架构与超参...
- 下一篇
NoSQL的四大种类
NoSQL数据库在整个数据库领域的江湖地位已经不言而喻。在大数据时代,虽然RDBMS很优秀,但是面对快速增长的数据规模和日渐复杂的数据模型,RDBMS渐渐力不从心,无法应对很多数据库处理任务,这时NoSQL凭借易扩展、大数据量和高性能以及灵活的数据模型成功的在数据库领域站稳了脚跟。 目前大家基本认同将NoSQL数据库分为四大类:键值存储数据库,文档型数据库,列存储数据库和图形数据库,其中每一种类型的数据库都能够解决关系型数据不能解决的问题。在实际应用中,NoSQL数据库的分类界限其实没有那么明显,往往会是多种类型的组合体。 主流nosql的详解:MongoDB、Hbase、Redis MongoDB MongoDB 是一个高性能,开源,无模式的文档型数据库,开发语言是C++。它在许多场景下可用于替代统的关系型数据库或键/值存储方式。 1.MongoDB特点 所用语言:C++特点:保留了SQL一些友好的特性(查询,索引)。使用许可: AGPL(发起者: Apache)协议: Custom, binary( BSON)Master/slave复制(支持自动错误恢复,使用 sets 复制)内...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Linux系统CentOS6、CentOS7手动修改IP地址
- Red5直播服务器,属于Java语言的直播服务器
- Docker安装Oracle12C,快速搭建Oracle学习环境
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS6,7,8上安装Nginx,支持https2.0的开启