項目構建分析和 webpack 優化實踐


加入新公司一個月,最近接手在做一個 chrom 瀏覽器插件的項目,開發過程中發現項目打包的時間很長,足足有30多秒,這是讓人很難接受的,而且構建的顯示了幾條包體積過大的提示信息:

可以看到,打包后有三個包超過了建議的體積,是什么導致了打包時間長和包的體積過大呢?

下面通過一些具體方法來分析原因和解決這個問題。

什么原因導致構建包變得這么大?

為了分析是什么導致構建包為什么會變得這么大,可以安裝 webpack-bundle-analyzer 插件,通過它可以直觀地查看構建包中所有項目的大小。

npm install —save-dev webpack-bundle-analyzer

對應的需要在 webpack.config.js 中做如下配置:

const { BundleAnalyzerPlugin } = require(‘webpack-bundle-analyzer’)

plugins: [
    ...,
    new BundleAnalyzerPlugin({
        analyzerPort: 8081,
    }),
]

配置完成后再次運行構建 npm start,瀏覽器會自動打開 http://127.0.0.1:8081,在網頁上可以看到構建包中每個文件的詳細信息。

從圖中可以找出影響體積的罪魁禍首有:

jquery、Moment、mammoth、html2canvas、xlsx、cpexcel、dexie

那么這些體積龐大的依賴庫都需要打到項目的運行包里面嗎?當然不是的。那我們逐步來優化這些依賴。

 減少 moment 大小 

moment 在包中占用了 545k 的體積,查看分析圖可以看到,庫文件中主要是各種用於支持語言版本的的locale文件,

但是項目中並不需要這部分功能,因此這部分數據是應該優化的。

查看項目中引入 moment 的方式

import moment form 'moment'

這樣會將整個 moment 包都導入到文件中,為了避免導入不必要的文件,可以這么寫:

import moment from 'moment/src/moment'

但是這么寫會有個問題,如果項目中有新成員加入,極大的可能他不會這樣寫,而是像原來一樣導入了整個 moment 包,因此為了避免這樣的問題,可以考慮在 webpack 中創建一個別名,這樣每次導入 moment 的時候就默認只導入文件夾下面的 moment.js 文件了,如下:

resolve: {
  alias: {
    moment: 'moment/src/moment',
  }
}

好了,重新啟動服務進行打包,報錯提示無法找到 ./locale:

查看 moment 的 官方 issue 發現這是一個存在已久的問題:moment.js 總是會加載 locales,還假定 locales 存在。你不能讓 moment 只加載日期操作函數。

官方提供的解決方案是把 package.json 中的 Moment 的版本改成 2.18.1, 如下:

不過在 Stack Overflow 上找到了另一種解決方案

簡單介紹一下 IgnorePlugin

  • 這是webpack內置插件
  • 作用:忽略第三方包指定目錄,讓這些指定目錄不要被打包進去

嘗試一下,在 webpack.config.js 中添加如下配置:

plugins:[
    // moment這個庫中,如果引用了./locale/目錄的內容,就忽略掉,不會打包進去
    new Webpack.IgnorePlugin(/\.\/locale/,/moment/),
]

按照上面的方法,減小了 moment 的打包體積,同時也避免了報錯,但是如果項目中需要用到語言包該怎么辦呢?很簡單,手動引入一下就可以了:

import moment from 'moment'
//手動引入所需要的語言包
import 'moment/locale/zh-cn';
moment.locale('zh-cn');

const r = moment().endOf('day').fromNow();
console.log(r);

 Ok,這么一來能夠顯示中文,又把不必要的語言包都忽略打包了,重新構建一下,看一下體積有沒有變化:

可以看到包已經縮減到了1.67M,打包時間縮短了29s(減少了4s),對應的觀察網頁上的顯示結果,moment 包的大小也從 545k 變成了 155.32k,小了很多,不是嗎?

 

使用 DllPlugin 加快打包速度

在用 Webpack 打包的時候,對於一些不經常更新的第三方庫,比如 react,lodash,vue ,可以將這些庫同項目代碼分離開來,提前打包,從而每次只打包項目自身的代碼,節省了打包時間。常用的方案是使用 DllPlugin。

如何使用 DllPlugin 呢?

首先在 webpack 文件夾下新建 webpack.dll.js文件

配置如下:

const webpack = require('webpack')
const path = require('path')

module.exports = {
  entry: {
    // manifest 的前綴名,這里會在webpack 文件下生成一個dll.manifest.json 文件
    dll: [
      'react',
      'react-dom',
      'antd',
      'classnames',
      'jquery',
      'xlsx',
      'mammoth',
      'html2canvas',
      'dexie',
      'cheerio', // 這些都是比較穩定,不常做修改的庫文件
    ],
  },
  output: {
        // 指定在 dist/static 下生成一個  dll.min.js 文件
      path: path.join(__dirname, '../dist/static/'),
    filename: '[name].min.js',
    library: '[name]',
  },
  plugins: [
    new webpack.DllPlugin({
        // 指定在當前文件夾下生成 manifest 文件
      path: path.resolve(__dirname, './dll.manifest.json'),
      name: '[name]',
      context: __dirname,
    }),
    // 壓縮,讓包更小一點
    new webpack.optimize.UglifyJsPlugin({ minimize: true }),
  ],
}

 配置完成后對應的需要在 webpack.config.js 中做如下修改:

const manifest = require('./dll.manifest.json')
plugins: [
   new webpack.DllReferencePlugin({
      context: __dirname,
       manifest,
   })
]

 然后在入口文件中引入 dll.min.js

 對應的,為了方便啟動,在 package.json 中添加快捷命令:

"scripts": {
    "dll": "webpack —config webpack/webpack.dll.js",
}

 到這里,DllPlugin 的相關配置就完成了,打包的時候執行 npm run dll  會在 webpack 目錄下生成 dll.manifest.json 文件,在 dist/static 目錄下會生成 dll.min.js 文件,在打包過程中, webpack 會將 webpack.dll.js 中配置包含的庫做一個索引,並寫在 dll.manifest.json 文件中,而引用 dll 的代碼在打包的時候,只要讀取這個 manifest 獲取對應的庫就可以了。

生成的 dll.manifest.json 文件

最后執行 npm run build 測試打包速度:

發現現在的打包時間不到19秒,相比於原來的33s減少了將近一半,對應兩個比較大的包體積也各自減少了2/3 還多。所以使用了 DllPlugin 之后,對項目的打包效率的提升還是很明顯的。

 

總結

項目最開始開始構建,打包后需要將近 4M 的空間,通過手動修改 moment 庫的引入方式和引入 webpack 的 DllPlugin 進行優化,打包后最終體積減少到了 1.2M,壓縮了一半多,對應打包時間也縮短了將近一半,所以通過 webpack 進行打包優化還是很有效果的。這給我的啟發是,在實際開發和打包上線過程中,需要細致地評估項目的構建體積和打包時間,通過 webpack-bundle-analyzer 可以直觀的觀察構建包的構成和體積分布,並且根據分析的結果有針對性地進行優化,以此來精簡項目體積,提升應用效率。當然,打包優化的方式不僅限於此,還可以通過 HappyPack 利用 Node 的多線程充分使用電腦多核來提升構建速度(但是實際效果不一定會變快),此外,還可以使用 webpack 的 externals 不打包某些文件,而在其他地方通過 cdn 引入,利用緩存下載 cnd 文件達到減少打包時間的目的,有興趣可以在項目中嘗試,相信你會有很多收獲。

 

參考:

DllPlugin | webpack 中文網

DllPlugin提升webpack打包速度

使用webpack的插件DllPlugin加快打包速度 - 前端下午茶

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM