vue-cli3 的快速插件開發

前言

不久前組內有大佬發佈了一個 vue-cli3 的 dll 包,做爲一個在 vue 項目內摸爬滾打的萌新,是時候該學習點兒新的技術了,因而在閒暇之餘,我拷貝了一份代碼,同時研究該如何從「零」開始編寫一個 dll 包(該部分以 webpack 的 dllPlugin 做爲例子)。html

瞭解文檔

通篇讀完官網的文檔,由於知識點比較多,而且沒有詳細的例子(指「傻瓜式教學式」),所以開發這第三方包的學習成本仍是有一些的,特別是 webpack-chain 和 node 的部分知識,這裏記錄總結了一些關鍵的點。前端

插件命名

最開始命名文件夾時我並無使用 vue-cli-plugin- 做爲文件名前綴,結果可想而知,vue invoke 一直提示找不到該包的信息。因而我去看了看源碼…… 在 @vue/cli/lib/invoke.js 內,其有一個關鍵的獲取包 Id 的方法 resolvePluginId,該方法在 @vue/cli-shared-utils/lib/pluginResolution.js,源碼以下:vue

exports.resolvePluginId = id => {
  // already full id
  // e.g. vue-cli-plugin-foo, @vue/cli-plugin-foo, @bar/vue-cli-plugin-foo
  if (pluginRE.test(id)) {
    return id
  }
  // scoped short
  // e.g. @vue/foo, @bar/foo
  if (id.charAt(0) === '@') {
    const scopeMatch = id.match(scopeRE)
    if (scopeMatch) {
      const scope = scopeMatch[0]
      const shortId = id.replace(scopeRE, '')
      return `${scope}${scope === '@vue/' ? `` : `vue-`}cli-plugin-${shortId}`
    }
  }
  // default short
  // e.g. foo
  return `vue-cli-plugin-${id}`
}
複製代碼

很明顯,使用 vue invoke 時其只會尋找含有 vue-cli-plugin- 做爲前綴的包,官網內在文檔的最後部分有作對應的說明(這個是後來纔看到的),原文以下:node

爲了讓一個 CLI 插件可以被其它開發者使用,你必須遵循 vue-cli-plugin-<name> 的命名約定將其發佈到 npm 上。webpack

所以 package.jsonname 字段符合規則便可。git

creator 和 service

官網開篇就介紹了兩個主要的部分:@vue/cli@vue/cli-service,首先是 @vue/cli 部分,這裏介紹了插件的目錄結構,所以咱們能夠根據此來搭一個插件框架:github

vue-cli-plugin-xxx
  ├── README.md
  ├── generator
  |  └── index.js
  ├── index.js
  ├── package.json
  ├── prompts
  |  └── index.js
  ├── service
  |  ├── config-file.js
  |  └── regist-command.js
  └── yarn.lock
複製代碼

接下來就詳細分析一下各部分的做用。web

generator

文檔分析

文檔中有提到,插件內的 generator 將會在兩種場景下被調用:vue-cli

  • 在一個項目的初始化建立過程當中,若是 CLI 插件做爲項目建立 preset 的一部分被安裝
  • 插件在項目建立好以後經過 vue invoke 獨立調用時被安裝

因爲開發的第三方插件使用場景多數在於更改已安裝的項目配置,preset 使用場景不是不少(建立項目時通常仍是手動配置,大多數狀況不會選擇去生成一個 ~/.vuerc),所以這邊僅處理使用手動調用 generator 的狀況。npm

觸發 generator 的方法有兩種:

  • vue invoke
  • vue add

下面就簡單介紹一下這兩個命令的區別。

vue invoke 指令

此指令的適用狀況爲已經經過 yarn 或者 npm 將包安裝至項目內,此時僅須要調用 vue invoke 便可。

注意:這裏的 packageName 爲不包含 vue-cli-plugin- 部分的剩餘包名,好比:發佈的包名爲 vue-cli-plugin-xxx,那麼此時使用命令即 vue invoke xxx

vue add

此指令的使用狀況爲項目內尚未安裝對應的包,使用方式同 vue invoke

注:若是包的源不對的話,請本身在後面加上包所在的 npm 源地址( --registry )

內容編寫

分析了這麼多,重點仍是 generator 內咱們應該寫點什麼,它影響的是什麼。好了,讓咱們來繼續看文檔(● ˃̶͈̀ロ˂̶͈́)੭ꠥ⁾⁾

generator 有三個參數,這裏就不細贅了,由於這裏不關注 preset 的配置,因此對咱們來講,有用的部分就只有第一個參數 api。首先咱們須要改動的部分即是項目內的 package.json 了,使用方法 extendPackage 便可,例子以下:

// 修改 `package.json` 裏的字段
// vue 部分的內容能夠不要
module.exports = (api, options, rootOptions) => {
  // 修改 `package.json` 裏的字段
  api.extendPackage({
    scripts: {
      test: 'vue-cli-service test'
    },
    vue: {
      pluginOptions: {
        test: {
          // 須要預打包的部分
          vendors: [],
          // 輸出文件名
          outputName: 'vendor.dll.js',
          // 輸出地址
          outputPath: './public/vendor',
          // 是否調用 cleanWebpackPlugin
          cleanCache: true
        }
      }
    }
  });
}
複製代碼

關於此處添加的 vue 字段在 invoke 後會自動補充至 vue.config.js 或者 package.json 內。

若是你配置了 promots 而且須要該部分的內容,那麼可使用第二個 options 參數去獲取配置的內容。(配置 .vuerc 的方法沒有嘗試,由於解構 + 默認值 + prompts 已經足夠了)

若是須要配置模板方面的參考官方源碼,感受配合 prompts 寫個模板插件也不錯。

友情提示:render() 函數內爲你的 template 模板基於當前文件夾所在的路徑。

prompts

該部分其實在這個項目內並無涉及,但仍是要提一下。官方文檔對於此部分在內建插件有詳細的說明(官方插件),第三方插件提到過一點:

這個文件應該導出一個用於 Inquirer.js 的問題的數組。這些被解析的答案對象會做爲選項被傳遞給插件的 generator。

所以,若是須要的狀況下,咱們得經過數組的形式編寫問答。例如:

module.exports = [
  {
    name: 'entry',
    message: "What's the output file's name?",
    type: 'input',
    default: 'vendor'
  }
]
複製代碼

此部分的配置結果會在 generator 部分的第二個參數捕捉到。

service

這部分就是配置的重點了,仍是根據官網來吧,官網有提到 3 個命令: chainWebpackconfigureWebpackregisterCommand。直接更改原有項目的配置並非很好(除非你頗有信心),所以咱們可使用 configureWebpack 來合併變動。

這部分總的來講作三件事:

  1. 更改用戶的 webpack 配置文件(也就是 vue.config.js)
  2. 向 cli-service 內註冊指令
  3. 爲註冊的指令指定模式

這裏從簡單的部分開始一一說明吧~(順序:三、一、2)

指定模式

嗯,這個最簡單了,畢竟官網文檔內有,理由也就不贅述了,代碼以下:

module.exports.defaultModes = {
  <your direct>: <target mode> } 複製代碼

其中 your direct 部分即先前的 generator 部分註冊的 script 腳本內,在 vue-cli-service 後面的那個指,好比,先前寫的是 test,那麼這裏註冊的指令也是 direct,mode 就根據實際狀況處理便可,通常使用 production 生產模式就沒啥問題。

更改用戶的配置文件

這裏就須要有 webpack-chain 的知識了。先看看 pluginAPI 內有些啥(傳送門),可能會用到的一些方法包括:

  • getCwd:獲取當前的工做目錄
  • resolve:至關於 path.resolve
  • registerCommand:註冊指令(有三個參數!)
  • chainWebpack:鏈式調用 webpack
  • configureWebpack:用於合併 webpack
  • resolveChainableWebpackConfig:用於解析 webpack

此處咱們使用 configureWebpack 來更改配置,同時使用 registerCommand 來註冊咱們的命令。這裏恰好對應的就是咱們的兩個文件 config-fileregist-command.js 了,對應開發便可。

configureWebpack

參考源碼的寫法,咱們能夠經過 options 參數獲取 invoke 生成的 vue.config.js 裏面的 pluginOptions 字段內的對應配置內容,配合 api.configureWebpack 來注入咱們的 webpack 配置。粗略的寫法爲:

api.configureWebpack(config => {
  // 增一個 plugins
  config.plugins.push(
    /* plugin 配置 */
  )
});
複製代碼
registerCommand

查看源碼的寫法,發現參數有三個,分別是:

  • api: pluginAPI 實例
  • options: 用來添加配置說明
  • fn: 回調函數,用於觸發執行的內容(好比運行一個 webpack 配置)

官方的寫法以下,咱們能夠據此模仿

api.registerCommand(
  "test",
  {
    description: "此爲指令的說明", // 指令意義
    usage: "vue-cli-service test", // 命令怎麼用
    options: {
      /* 參數說明 */
    }
  },
  async args => {
    /* 能夠寫個 chain-webpack 而後調用,或者乾點其它的 */
  }
);
複製代碼

部分問題總結

1. demo 內的 dll 打包位置問題

使用默認的打包位置: public/vendor 會產生一個問題,就是 build 文件內會包含此 vendor 文件夾,咱們注入的一些配置不生效。

解決方法:將默認的 public/vendor 打包位置改改,只要不是生成在 public 文件夾內的都沒問題,可看 vue-cli 官網的 public 部分的解釋。

2. webpack 函數

目前第二個回調參數的使用場景不是很明確(其實不用回調也能夠,外層關閉對應的 log 便可)

3. configureWebpack 只能用 push?

我嘗試過本身新建立一個 webpack-chain 而後返回(源碼上好像會對返回值進行判斷,若是有那麼會調用 merge 方法),按理來講返回一個 config.toConfig() 應該 沒問題,可是行不通,目前仍然用的是 push 方法

demo

傳送門

參考文檔

--by kazehaiya

相關文章
相關標籤/搜索