說(shuō)說(shuō)提高Webpack的構(gòu)建速度的手段有哪些?
本文轉(zhuǎn)載自微信公眾號(hào)「JS每日一題」,作者灰灰。轉(zhuǎn)載本文請(qǐng)聯(lián)系JS每日一題公眾號(hào)。
一、背景
隨著我們的項(xiàng)目涉及到頁(yè)面越來(lái)越多,功能和業(yè)務(wù)代碼也會(huì)隨著越多,相應(yīng)的 webpack 的構(gòu)建時(shí)間也會(huì)越來(lái)越久
構(gòu)建時(shí)間與我們?nèi)粘i_(kāi)發(fā)效率密切相關(guān),當(dāng)我們本地開(kāi)發(fā)啟動(dòng) devServer 或者 build 的時(shí)候,如果時(shí)間過(guò)長(zhǎng),會(huì)大大降低我們的工作效率
所以,優(yōu)化webpack 構(gòu)建速度是十分重要的環(huán)節(jié)
二、如何優(yōu)化
常見(jiàn)的提升構(gòu)建速度的手段有如下:
- 優(yōu)化 loader 配置
- 合理使用 resolve.extensions
- 優(yōu)化 resolve.modules
- 優(yōu)化 resolve.alias
- 使用 DLLPlugin 插件
- 使用 cache-loader
- terser 啟動(dòng)多線程
- 合理使用 sourceMap
優(yōu)化loader配置
在使用loader時(shí),可以通過(guò)配置include、exclude、test屬性來(lái)匹配文件,接觸include、exclude規(guī)定哪些匹配應(yīng)用loader
如采用 ES6 的項(xiàng)目為例,在配置 babel-loader時(shí),可以這樣:
- module.exports = {
- module: {
- rules: [
- {
- // 如果項(xiàng)目源碼中只有 js 文件就不要寫成 /\.jsx?$/,提升正則表達(dá)式性能
- test: /\.js$/,
- // babel-loader 支持緩存轉(zhuǎn)換出的結(jié)果,通過(guò) cacheDirectory 選項(xiàng)開(kāi)啟
- use: ['babel-loader?cacheDirectory'],
- // 只對(duì)項(xiàng)目根目錄下的 src 目錄中的文件采用 babel-loader
- include: path.resolve(__dirname, 'src'),
- },
- ]
- },
- };
合理使用 resolve.extensions
在開(kāi)發(fā)中我們會(huì)有各種各樣的模塊依賴,這些模塊可能來(lái)自于自己編寫的代碼,也可能來(lái)自第三方庫(kù), resolve可以幫助webpack從每個(gè) require/import 語(yǔ)句中,找到需要引入到合適的模塊代碼
通過(guò)resolve.extensions是解析到文件時(shí)自動(dòng)添加拓展名,默認(rèn)情況如下:
- module.exports = {
- ...
- extensions:[".warm",".mjs",".js",".json"]
- }
當(dāng)我們引入文件的時(shí)候,若沒(méi)有文件后綴名,則會(huì)根據(jù)數(shù)組內(nèi)的值依次查找
當(dāng)我們配置的時(shí)候,則不要隨便把所有后綴都寫在里面,這會(huì)調(diào)用多次文件的查找,這樣就會(huì)減慢打包速度
優(yōu)化 resolve.modules
resolve.modules 用于配置 webpack 去哪些目錄下尋找第三方模塊。默認(rèn)值為['node_modules'],所以默認(rèn)會(huì)從node_modules中查找文件 當(dāng)安裝的第三方模塊都放在項(xiàng)目根目錄下的 ./node_modules目錄下時(shí),所以可以指明存放第三方模塊的絕對(duì)路徑,以減少尋找,配置如下:
- module.exports = {
- resolve: {
- // 使用絕對(duì)路徑指明第三方模塊存放的位置,以減少搜索步驟
- // 其中 __dirname 表示當(dāng)前工作目錄,也就是項(xiàng)目根目錄
- modules: [path.resolve(__dirname, 'node_modules')]
- },
- };
優(yōu)化 resolve.alias
alias給一些常用的路徑起一個(gè)別名,特別當(dāng)我們的項(xiàng)目目錄結(jié)構(gòu)比較深的時(shí)候,一個(gè)文件的路徑可能是./../../的形式
通過(guò)配置alias以減少查找過(guò)程
- module.exports = {
- ...
- resolve:{
- alias:{
- "@":path.resolve(__dirname,'./src')
- }
- }
- }
使用 DLLPlugin 插件
DLL全稱是 動(dòng)態(tài)鏈接庫(kù),是為軟件在winodw中實(shí)現(xiàn)共享函數(shù)庫(kù)的一種實(shí)現(xiàn)方式,而Webpack也內(nèi)置了DLL的功能,為的就是可以共享,不經(jīng)常改變的代碼,抽成一個(gè)共享的庫(kù)。這個(gè)庫(kù)在之后的編譯過(guò)程中,會(huì)被引入到其他項(xiàng)目的代碼中
使用步驟分成兩部分:
- 打包一個(gè) DLL 庫(kù)
- 引入 DLL 庫(kù)
打包一個(gè) DLL 庫(kù)
webpack內(nèi)置了一個(gè)DllPlugin可以幫助我們打包一個(gè)DLL的庫(kù)文件
- module.exports = {
- ...
- plugins:[
- new webpack.DllPlugin({
- name:'dll_[name]',
- path:path.resolve(__dirname,"./dll/[name].mainfest.json")
- })
- ]
- }
引入 DLL 庫(kù)
使用 webpack 自帶的 DllReferencePlugin 插件對(duì) mainfest.json 映射文件進(jìn)行分析,獲取要使用的DLL庫(kù)
然后再通過(guò)AddAssetHtmlPlugin插件,將我們打包的DLL庫(kù)引入到Html模塊中
- module.exports = {
- ...
- new webpack.DllReferencePlugin({
- context:path.resolve(__dirname,"./dll/dll_react.js"),
- mainfest:path.resolve(__dirname,"./dll/react.mainfest.json")
- }),
- new AddAssetHtmlPlugin({
- outputPath:"./auto",
- filepath:path.resolve(__dirname,"./dll/dll_react.js")
- })
- }
使用 cache-loader
在一些性能開(kāi)銷較大的 loader之前添加 cache-loader,以將結(jié)果緩存到磁盤里,顯著提升二次構(gòu)建速度
保存和讀取這些緩存文件會(huì)有一些時(shí)間開(kāi)銷,所以請(qǐng)只對(duì)性能開(kāi)銷較大的 loader 使用此loader
- module.exports = {
- module: {
- rules: [
- {
- test: /\.ext$/,
- use: ['cache-loader', ...loaders],
- include: path.resolve('src'),
- },
- ],
- },
- };
terser 啟動(dòng)多線程
使用多進(jìn)程并行運(yùn)行來(lái)提高構(gòu)建速度
- module.exports = {
- optimization: {
- minimizer: [
- new TerserPlugin({
- parallel: true,
- }),
- ],
- },
- };
合理使用 sourceMap
打包生成 sourceMap 的時(shí)候,如果信息越詳細(xì),打包速度就會(huì)越慢。對(duì)應(yīng)屬性取值如下所示:
三、總結(jié)
可以看到,優(yōu)化webpack構(gòu)建的方式有很多,主要可以從優(yōu)化搜索時(shí)間、縮小文件搜索范圍、減少不必要的編譯等方面入手
參考文獻(xiàn)
- https://github.com/ly2011/blog/issues/44
- https://xie.infoq.cn/article/541418eb82a674741a0ad8865
- https://zhuanlan.zhihu.com/p/139498741
- https://vue3js.cn/interview