Create by jsliang on 2020-10-20 15:00:19
Recently revised in 2020-12-07 07:50:10
不折腾的前端,和咸鱼有什么区别
Webpack 的优化瓶颈,主要是 2 个方面:
- Webpack 的构建过程太花时间
- Webpack 打包的结果体积太大
本文从这 2 个角度出发,收集一些相关优化资料。
resolve.modules
用于配置 Webpack
去哪些目录下寻找第三方模块,默认是 ['node_modules']
。
但是,它会先去当前目录的 ./node_modules
查找,没有的话再去 ../node_modules
,最后到根目录 —— 即 npm
查找包的规则。
所以可以直接指定项目根目录,这样代码就不需要一层一层查找。
resolve: {
modules: [path.resolve(__dirname, 'node_modules')],
}
在导入没带文件后缀的路径时,Webpack
会自动带上后缀去尝试询问文件是否存在,而 resolve.extensions
用于配置尝试后缀列表;默认为 extensions:['js', 'json']
。
当遇到 require('./data')
时 Webpack
会先尝试寻找 data.js
,没有再去找 data.json
;如果列表越长,或者正确的后缀越往后,尝试的次数就会越多。
所以在配置时为提升构建优化需遵守:
- 频率出现高的文件后缀优先放在前面。
- 列表尽可能的少,例如只有 3 个:
js
、jsx
、json
。 - 书写导入语句时,尽量写上后缀名。
以 babel-loader
为例,可以通过 include
和 exclude
帮助我们避免 node_modules
这类庞大文件夹。
即通过 include
告诉 Webpack
哪些我们是需要检测的,通过 exclude
告诉 Webpack
哪些我们是不需要检测的(例如已经收拾过的静态资源)
在 babel-loader
开启 cache
后,将 loader
的编译结果写进硬盘缓存,再次构建如果文件没有发生变化则会直接拉取缓存。
uglifyjs-webpack-plugin
通过这个插件也可以解决缓存问题。
注:具体的要根据当前的
Webpack
版本,Loader
和Plugin
表示Webpack
每次更新都会淘汰一批没有跟进维护的Loader
和Plugin
。就跟大佬还在持续学习,你几年没学习之后,遇到金融危机被淘汰的风险就高了。
由于有大量文件需要解析和处理,构建是文件读写和计算密集型的操作,特别是当文件数量变多后,Webpack
构建慢的问题会显得严重。
文件读写和计算操作是无法避免的,那能不能让 Webpack
同一时刻处理多个任务,发挥多核 CPU 电脑的威力,以提升构建速度呢?
Happypack
可以将任务分解成多个子进程去并发执行,大大提升打包效率。
除此之外 thread-loader
和 Happypack
一样,但是配置比较简单。
Happypack
已经不维护了。Github - Happypack
因为自带的 UglifyjsWebpackPlugin
压缩插件是单线程运行的,而 TerserWebpackPlugin
可以并发运行压缩功能(多进程)。
所以通过 TerserWebpackPlugin
代替自带的 UglifyjsWebpackPlugin
插件。
通过 DllPlugin
或者 Externals
进行静态依赖包的分离。
由于 CommonsChunkPlugin
每次构建会重新构建一次 vendor
,所以出于效率考虑,使用 DllPlugin
将第三方库单独打包到一个文件中,只有依赖自身发生版本变化时才会重新打包。
在 Webpack
中,到底什么是代码分离?代码分离允许你把代码拆分到多个文件中。如果使用得当,你的应用性能会提高很多。因为浏览器能缓存你的代码。
每当你做出一次修改,包含修改的文件需要被所有访问你网站的人重新下载。但你并不会经常修改应用的依赖库。
如果你能把那些依赖库拆分到完全分离的文件中,即使业务逻辑发生了更改,访问者也不需要再次下载依赖库,直接使用之前的缓存就可以了。
由于有了 SplitChunksPlugin
,你可以把应用中的特定部分移至不同文件。如果一个模块在不止一个 chunk
中被使用,那么利用代码分离,该模块就可以在它们之间很好地被共享。
- JS 压缩:
UglifyjsWebpackPlugin
- HTML 压缩:
HtmlWebpackPlugin
- CSS 压缩:
MiniCssExtractPlugin
- 图片压缩:
image-webpack-loader
- Gzip 压缩:不包括图片
通过 ES6 的 import/export
来检查未引用代码,以及 sideEffects
来标记无副作用代码,最后用 UglifyJSPlugin
来做 Tree Shaking
,从而删除冗余代码。
Scope Hoisting
是 Webpack3 的新功能,直译为 「作用域提升」,它可以让 Webpack 打包出来的 「代码文件更小」,「运行速度更快」。
熟悉 JavaScript 都应该知道 「函数提升」 和 「变量提升」 ,JavaScript 会把函数和变量声明提升到当前作用域的顶部。
「作用域提升」 也类似于此,Webpack 会把引入的 js 文件 “提升到” 它的引入者顶部。
Scope Hoisting
的实现原理其实很简单:分析出模块之间的依赖关系,尽可能将打散的模块合并到一个函数中,前提是不能造成代码冗余。因此「只有那些被引用了一次的模块才能被合并」。
由于 Scope Hoisting
需要分析出模块之间的依赖关系,因此源码「必须采用 ES6
模块化语句」,不然它将无法生效。原因和 Tree Shaking
中介绍的类似。
- 什么是 代码分割(code splitting)?
代码分割是指:将脚本中无需立即调用的代码在代码构建时转变为异步加载的过程。
在 Webpack 构建时,会避免加载已声明要异步加载的代码,异步代码会被单独分离出一个文件,当代码实际调用时被加载至页面。
代码分割技术的核心是 异步加载资源。
可喜的是,浏览器允许我们这么做,W3C stage 3
规范: whatwg/loader 对其进行了定义:你可以通过 import()
关键字让浏览器在程序执行时异步加载相关资源。
在 Vue 中,可以直接使用 import()
关键字做到这一点,而在 React 中,你需要使用 react-loadable
去完成同样的事。
- progress-bar-webpack-plugin:在终端底部,将会有一个构建的进度条,可以让你清晰的看见构建的执行进度。
- webpack-build-notifier:在构建完成时,能够像微信、Lark 这样的 APP 弹出消息的方式,提示构建已经完成。
- webpack-dashboard:对 Webpack 原始的构建输出不满意的话,也可以使用这样一款 Plugin 来优化你的输出界面。
- speed-measure-webpack-plugin:该插件可以测量各个插件和
loader
所花费的时间。 webpack-bundle-analyzer
:可视化分析。通过矩阵树图的方式将包内各个模块的大小和依赖关系呈现出来。webpack-chart
webpack-analyse
2019 年文章:
- Webpack优化——将你的构建效率提速翻倍【阅读建议:10min】
- 性能优化篇---Webpack构建速度优化【阅读建议:10min】
- 使用webpack4提升180%编译速度【阅读建议:10min】
- 多进程并行压缩代码【阅读建议:5min】
- webpack 4: Code Splitting和chunks切分优化【阅读建议:5min】
2018 年文章:
- Tree-Shaking性能优化实践 - 原理篇【阅读建议:10min】
- 体积减少80%!释放webpack tree-shaking的真正潜力【阅读建议:10min】
- 你的Tree-Shaking并没什么卵用【阅读建议:5min】
- webpack 如何通过作用域分析消除无用代码【阅读建议:5min】
- 让你的Webpack起飞—考拉会员后台Webpack优化实战【阅读建议:5min】
- webpack dllPlugin打包体积和速度优化【阅读建议:5min】
- webpack优化之code splitting【阅读建议:5min】
2017 年文章:
- Webpack 打包优化之速度篇【阅读建议:5min】
- 加速Webpack-缩小文件搜索范围【阅读建议:5min】
- Webpack 大法之 Code Splitting【阅读建议:5min】
jsliang 的文档库由 梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议 进行许可。
基于 https://github.com/LiangJunrong/document-library 上的作品创作。
本许可协议授权之外的使用权限可以从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处获得。