基於webpack工程化的思考

NO IMAGE

本文章是在基於webpack多入口配置工程並且項目需要持續迭代作為基礎去討論的,單頁面不作討論。文章適合對webpack有一定了解的人看,主要討論工程化中關於緩存利用率的問題。

一、如何打包文件讓緩存利用率增高

在webpack中,我們通常會打包一個vendor和一個common文件作為基礎包,vendor通常是將npm中的依賴打包、而common則是讓達到一定引用次數的模塊進行打包。具體配置如下:

基於webpack工程化的思考

基於webpack工程化的思考

以上的配置,在持續迭代中,是不利於持久性緩存的,因為需要持續迭代,不可避免的是業務文件的修改,前端做緩存主要依靠的是通過給文件名加上hash值去控制文件緩存,而webpack1中一旦修改了某個文件,會導致整體的文件hash值的變化,包括vendor和common的hash值,但是這一個缺點在webpack2以後就被處理得比較好了,可以用HashedModuleIdsPlugin穩定模塊的id,以保證vendor在單個業務文件修改時,其他文件的hash值保持穩定。

但是在持續增長的業務中,這樣的方式對持久化就是可靠的嗎?如果你在增量迭代時,突然需要引入某一些npm包呢?導致vendor的hash值在這個時候也會發生變化,所有的用戶不得不重新下載一個龐大的vendor文件。

根據這種場景我們可以如何去優化這個緩存呢?這裡我採用的方案是,將整個項目依賴的基礎包,單獨放在vendor中進行手動配置打包,在入口配置中加入自己手動配置的vendor列表,這一個列表是整個項目最基礎依賴的包。

例如:在一個多入口vue建立的項目中,我們項目幾乎每個入口頁面都要引入vue,那麼我們的vendor列表中就可以配置上vue

基於webpack工程化的思考

基於webpack工程化的思考

那麼vendor包中就只會將vue打包進vendor,而其餘的模塊則按照引用次數將包打包進common中,這麼做的好處是什麼?在項目迭代中,vendor的打包是我們可以控制的,也就是這個列表我們不修改,則vendor的hash值不變,也就是說在用戶不清除緩存的情況下,我們的vendor包可以一直在緩存中,對於頁面的性能來說是有保證的,變化的模塊也僅僅在common模塊和其他的業務文件。

比如,在vue的項目中,我們集成了vuex、rxjs、axios等庫,在多頁面開發中這些庫基本在每一個入口中都會有引用到,那麼我們是不是可以將vendor的列表配置成[ ‘vue’, ‘vuex’, ‘rxjs’, ‘axios’ ],通過commonPlugin則可以將列表中的模塊打包成vendor,在迭代中,其他npm包的引入並不會影響到vendor hash值的改變,等到你需要更新vendor時,則可以手動往列表中添加依賴。

二、dll文件構建自動化

dll文件是在開發時,先對所有的npm包預先打包,每一個入口都會引入,之後根據manifest去對npm包資源進行引用,這樣做,在開發時就不需要對npm包進行打包構建,節省一部分時間,具體可google。

基於webpack工程化的思考

這裡就會涉及到一個問題,每一次npm依賴更新了,都需要重新進行dll文件的打包。作為一個程序員,很顯然,這麼做不夠偷懶。那我們可以如何去做dll文件構建的自動化呢?這裡提供一下我的思路。

一般我們在開始時,通常使用npm script進行項目構建、而dll文件需要單獨運行命令,之後再構建項目

webpack --config dll.config.js

webpack-dev-server --config dev.config.js

因為涉及到兩條命令,所以我選擇了使用shell對兩個命令合併

// npm run start命令行執行dev.sh腳本
sh bin/dev.sh
#!/bin/bash
### 思路
###1、在首次構建時,需要生成依賴的數量,並將數據重定向到文件中,用於二次構建時依賴數量的匹配
###2、二次構建時,會先判斷記錄了依賴數量的文件是否存在,存在則讀取數量進行現有依賴數量的匹配,若依賴數量無變化,則認為dll文件不需要構建,直接運行項目的構建,若依賴數量變化,則重新構建dll文件

### 用於本地開發時自動維護dll文件
rootPath=`pwd`
packagePath=${rootPath}'/package.json'
dllValidatePath=${rootPath}'/build/vendor.dll.validate.txt'

### 讀取package.json的dependencies與devDependencies對應的行數
dependenciesRow=`grep -n "dependencies" $packagePath | cut  -d  ":"  -f  1`
devDependenciesRow=`grep -n "devDependencies" $packagePath | cut  -d  ":"  -f  1`

### 依賴行數(以此判斷依賴是否增減)
rows=$[$devDependenciesRow-$dependenciesRow]

### 判斷vendor.dll.validate.txt文件是否存在
if [ -e $dllValidatePath ]; then

  ### 獲取之前的依賴行數
  oldRows=`cat $dllValidatePath`

  if [ $oldRows == $rows ]; then
    ### 依賴數量相等則直接構建
    npm run dev
  else
    echo $rows > $dllValidatePath
    ### 不等,重新創建vendor.dll.js,並將新的行數寫到vendor.dll.validate.txt
    npm run dll
    npm run dev
  fi

else
  rm -rf build
  mkdir build
  touch $dllValidatePath
  echo $rows > $dllValidatePath
  npm run dll
  npm run dev
fi

上面是通過依賴數量的變化來做其實是有點bug的,如果依賴增加了之後再減少到相同數量,dll文件並不會重新構建,這時需要自己手動構建一次,所以,嚴謹一些應該是要對依賴列表進行前後對比才可以確定dll是否需要變化shell相應的也會更復雜一些吧,以後有時間優化一下這裡的邏輯。

注意:dll文件,最好不要壓縮,因為壓縮插件會將console打印的語句去除,會導致開發時框架或者庫的錯誤提示或者警告丟失。

三、庫的引用

不知道大家有沒一種情況,就是某一些庫,只是那麼兩三個入口需要使用,但是卻被打包進common中。比如一些即時聊天服務,通常需要引入一些script或者css,而你的common配置中,超過兩個引用次數就會將包打進common中,導致common過大,而每一個入口通常都需要引入common,並且這些外部引用的庫會隨著common的打包變化而重新下載,這就會導致用戶無法持久緩存這部分這種庫。

當然,我們可以通過增加common的模塊引用次數下限去將這種庫排除在外,的確可以,但是這樣對common的控制就會受限,這不是我想的,我想common的配置,與這種業務性非常強的庫抽離出來。

先看一下HtmlWebpackPlugin的配置

基於webpack工程化的思考

基於webpack工程化的思考

基於webpack工程化的思考

基於webpack工程化的思考

基於webpack工程化的思考

這裡我們通過這個插件的自定義配置,在html中通過標記將script或者css文件以絕對路徑的方式輸出到結果中,這樣我們就不需要通過webpack對這種稍微大一些又不想打包進common的包進行引用了,直接通過全局script標籤的方式進行引入,以一定的規範放置文件,根據文件的版本做緩存的控制。

四、總結

以上是我對於webpack多入口配置的一些思考以及建議,如有錯誤,歡迎及時指正。下面是github項目地址webpack3-vue-cli

相關文章

HybirdApp模式下,js如何與客戶端交互

RxJS實踐,Vue如何集成RxJS

React使用新版Context構建組件樹工具注入

如何在Koa集成Bigpipe首屏渲染服務