擼一個 webpack 外掛,希望對大家有所幫助

前端小智發表於2021-12-16
有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

最近,陸陸續續搞 了一個 UniUsingComponentsWebpackPlugin 外掛(下面介紹),這是自己第三個開源專案,希望大家一起來維護,一起 star 呀,其它兩個:

背景

第一個痛點

用 uniapp開發小程式的小夥伴應該知道,我們在 uniapp 中要使用第三方 UI 庫(vant-weappiView-weapp)的時候 ,想要在全域性中使用,需要在 src/pages.json 中的 usingComponents 新增對應的元件宣告,如:

// src/pages.json
"usingComponents": {
    "van-button": "/wxcomponents/@vant/weapp/button/index",
  }

但在開發過程中,我們不太清楚需要哪些元件,所以我們可能會全部宣告一遍(PS:這在做公共庫的時候更常見),所以我們得一個個的寫,做為程式設計師,我們絕不允許使用這種笨方法。這是第一個痛點

第二個痛點

使用第三方元件,除了在 src/pages.json 還需要在對應的生產目錄下建立 wxcomponents,並將第三方的庫拷貝至該檔案下,這個是 uniapp 自定義的,詳細就見:https://uniapp.dcloud.io/fram...

這是第二個痛點

第三個痛點

第二痛點,我們將整個UI庫拷貝至 wxcomponents,但最終釋出的時候,我們不太可能全都用到了裡面的全域性元件,所以就將不必要的元件也釋出上去,增加程式碼的體積。

有的小夥伴就會想到,那你將第三方的庫拷貝至 wxcomponents時候,可以只拷使用到的就行啦。是這理沒錯,但元件裡面可能還會使用到其它元件,我們還得一個個去看,然後一個個引入,這又回到了第一個痛點了

有了這三個痛點,必須得有個外掛來做這些傻事,處理這三個痛點。於是就有 UniUsingComponentsWebpackPlugin 外掛,這個webpack 外掛主要解決下面幾個問題:

  • 配置第三方庫後可以自動引入其下的原生元件,而無需手動配置
  • 生產構建時可以自動剔除沒有使用到的原生元件

webpack 外掛

webpack 的外掛體系是一種基於 Tapable 實現的強耦合架構,它在特定時機觸發鉤子時會附帶上足夠的上下文資訊,外掛定義的鉤子回撥中,能也只能與這些上下文背後的資料結構、介面互動產生 side effect,進而影響到編譯狀態和後續流程。

從形態上看,外掛通常是一個帶有 apply 函式的類:

class SomePlugin {
    apply(compiler) {
    }
}

Webpack 會在啟動後按照註冊的順序逐次呼叫外掛物件的 apply 函式,同時傳入編譯器物件 compiler ,外掛開發者可以以此為起點觸達到 webpack 內部定義的任意鉤子,例如:

class SomePlugin {
    apply(compiler) {
        compiler.hooks.thisCompilation.tap('SomePlugin', (compilation) => {
        })
    }
}

注意觀察核心語句 compiler.hooks.thisCompilation.tap,其中 thisCompilation 為 tapable 倉庫提供的鉤子物件;tap 為訂閱函式,用於註冊回撥。

Webpack 的外掛體系基於 tapable 提供的各類鉤子展開,所以有必要先熟悉一下 tapable 提供的鉤子型別及各自的特點。

到這裡,就不做繼續介紹了,關於外掛的更多 詳情可以去官網瞭解。

這裡推薦 Tecvan 大佬寫的 《Webpack 外掛架構深度講解》https://mp.weixin.qq.com/s/tX...

實現思路

UniUsingComponentsWebpackPlugin 外掛主要用到了三個 compiler 鉤子。

第一個鉤子是 environment:

compiler.hooks.environment.tap(
      'UniUsingComponentsWebpackPlugin',
      async () => {
        // todo someing
      }
    );

這個鉤子主要用來自動引入其下的原生元件,這樣就無需手動配置。解決第一個痛點

第二個鉤子 thisCompilation,這個鉤子可以獲得 compilation,能對最終打包的產物進行操作:

compiler.hooks.thisCompilation.tap(
      'UniUsingComponentsWebpackPlugin',
      (compilation) => {
        // 新增資源 hooks
        compilation.hooks.additionalAssets.tapAsync(
          'UniUsingComponentsWebpackPlugin',
          async (cb) => {
            await this.copyUsingComponents(compiler, compilation);
            cb();
          }
        );
      }
    );

所以這個勾子用來將 node_modules 下的第三庫拷貝到我們生產 dist 目錄裡面的 wxcomponents解決第二個痛點

ps:這裡也可直接用現有的 copy-webpack-plugin 外掛來實現。

第三個鉤子 done,表示 compilation 執行完成:

    if (process.env.NODE_ENV === 'production') {
      compiler.hooks.done.tapAsync(
        'UniUsingComponentsWebpackPlugin',
        (stats, callback) => {
          this.deleteNoUseComponents();
          callback();
        }
      );
    }

執行完成後,表示我們已經生成 dist 目錄了,可以讀取檔案內容,分析,獲取哪些元件被使用了,然後刪除沒有使用到元件對應的檔案。這樣就可以解決我們第三個痛點了

PS:這裡我判斷只有在生產環境下才會 剔除,開發環境沒有,也沒太必要。

使用

安裝

npm install uni-using-components-webpack-plugin --save-dev

然後將外掛新增到 WebPack Config 中。例如:

const UniUsingComponentsWebpackPlugin = require("uni-using-components-webpack-plugin");

module.exports = {
  plugins: [
    new UniUsingComponentsWebpackPlugin({
        patterns: [
        {
            prefix: 'van',
            module: '@vant/weapp',
        },
        {
            prefix: 'i',
            module: 'iview-weapp',
        },
        ],
    })
  ],
};
注意:uni-using-components-webpack-plugin 只適用在 UniApp 開發的小程式。

引數

NameTypeDescription
patterns{Array<Object>}為外掛指定相關

Patterns

moduleprefix
模組名元件字首

module 是指 package.json 裡面的 name,如使用是 Vant 對應的 module@vant/weapp,如果使用是 iview,剛對應的 moduleiview-weapp,具體可看它們各自的 package.json

prefix 是指元件的字首,如 Vant 使用是 van 開頭的字首,iview 使用是 i 開頭的字首,具體可看它們各自的官方文件。

PS: 這裡得吐曹一下 vant,叫別人使用 van 的字首,然後自己元件裡面宣告子元件時,卻沒有使用 van 字首,如 picker 元件,它裡面的 JSON 檔案是這麼寫的:

{
  "component": true,
  "usingComponents": {
    "picker-column": "../picker-column/index",
    "loading": "../loading/index"
  }
}

picker-columnloading 都沒有帶 van 字首,因為這個問題,在做 自動剔除 功能中,我是根據 字首來判斷使用哪些元件的,由於這裡的 loadingpicker-column 沒有加字首,所以就被會刪除,導致最終的 picker 用不了。為了解決這個問題,增加了不少工作量。

希望 Vant 官方後面的版本能優化一下。

總結

本文通用自定義 Webpack 外掛來實現日常一些技術優化需求。主要為大家介紹了 Webpack 外掛的基本組成和簡單架構,通過三個痛點,引出了 uni-using-components-webpack-plugin 外掛,並介紹了使用方式,實現思路。

最後,關於 Webpack 外掛開發,還有更多知識可以學習,建議多看看官方文件《Writing a Plugin》進行學習。

程式碼部署後可能存在的BUG沒法實時知道,事後為了解決這些BUG,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug

交流

有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。

本文 GitHub https://github.com/qq44924588... 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

相關文章