基於webpack工程化的思考

BUG給我滾發表於2019-03-01

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

一、如何打包檔案讓快取利用率增高

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

vendor

common

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

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

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

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

webpack入口配置中加上這一個鍵值對

讓模組抽取通過入口名為vendor的列表進行打包

那麼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。

dll

這裡就會涉及到一個問題,每一次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的配置

html-webpack-plugin配置

scriptComponents

目錄

標記

構建後

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

四、總結

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

相關文章