不久前組內有大佬發佈了一個 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.json
的 name
字段符合規則便可。git
官網開篇就介紹了兩個主要的部分:@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 將會在兩種場景下被調用:vue-cli
vue invoke
獨立調用時被安裝因爲開發的第三方插件使用場景多數在於更改已安裝的項目配置,preset
使用場景不是不少(建立項目時通常仍是手動配置,大多數狀況不會選擇去生成一個 ~/.vuerc),所以這邊僅處理使用手動調用 generator 的狀況。npm
觸發 generator
的方法有兩種:
vue invoke
vue add
下面就簡單介紹一下這兩個命令的區別。
此指令的適用狀況爲已經經過 yarn 或者 npm 將包安裝至項目內,此時僅須要調用 vue invoke 便可。
注意:這裏的 packageName 爲不包含
vue-cli-plugin-
部分的剩餘包名,好比:發佈的包名爲vue-cli-plugin-xxx
,那麼此時使用命令即vue invoke xxx
此指令的使用狀況爲項目內尚未安裝對應的包,使用方式同 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 模板基於當前文件夾所在的路徑。
該部分其實在這個項目內並無涉及,但仍是要提一下。官方文檔對於此部分在內建插件有詳細的說明(官方插件),第三方插件提到過一點:
這個文件應該導出一個用於 Inquirer.js 的問題的數組。這些被解析的答案對象會做爲選項被傳遞給插件的 generator。
所以,若是須要的狀況下,咱們得經過數組的形式編寫問答。例如:
module.exports = [
{
name: 'entry',
message: "What's the output file's name?",
type: 'input',
default: 'vendor'
}
]
複製代碼
此部分的配置結果會在 generator 部分的第二個參數捕捉到。
這部分就是配置的重點了,仍是根據官網來吧,官網有提到 3 個命令: chainWebpack
、configureWebpack
、registerCommand
。直接更改原有項目的配置並非很好(除非你頗有信心),所以咱們可使用 configureWebpack
來合併變動。
這部分總的來講作三件事:
這裏從簡單的部分開始一一說明吧~(順序:三、一、2)
嗯,這個最簡單了,畢竟官網文檔內有,理由也就不贅述了,代碼以下:
module.exports.defaultModes = {
<your direct>: <target mode> } 複製代碼
其中 your direct
部分即先前的 generator 部分註冊的 script 腳本內,在 vue-cli-service
後面的那個指,好比,先前寫的是 test
,那麼這裏註冊的指令也是 direct
,mode 就根據實際狀況處理便可,通常使用 production
生產模式就沒啥問題。
這裏就須要有 webpack-chain 的知識了。先看看 pluginAPI 內有些啥(傳送門),可能會用到的一些方法包括:
此處咱們使用 configureWebpack
來更改配置,同時使用 registerCommand
來註冊咱們的命令。這裏恰好對應的就是咱們的兩個文件 config-file
和 regist-command.js
了,對應開發便可。
參考源碼的寫法,咱們能夠經過 options 參數獲取 invoke 生成的 vue.config.js
裏面的 pluginOptions
字段內的對應配置內容,配合 api.configureWebpack
來注入咱們的 webpack 配置。粗略的寫法爲:
api.configureWebpack(config => {
// 增一個 plugins
config.plugins.push(
/* plugin 配置 */
)
});
複製代碼
查看源碼的寫法,發現參數有三個,分別是:
官方的寫法以下,咱們能夠據此模仿
api.registerCommand(
"test",
{
description: "此爲指令的說明", // 指令意義
usage: "vue-cli-service test", // 命令怎麼用
options: {
/* 參數說明 */
}
},
async args => {
/* 能夠寫個 chain-webpack 而後調用,或者乾點其它的 */
}
);
複製代碼
使用默認的打包位置: public/vendor
會產生一個問題,就是 build 文件內會包含此 vendor 文件夾,咱們注入的一些配置不生效。
解決方法:將默認的 public/vendor
打包位置改改,只要不是生成在 public 文件夾內的都沒問題,可看 vue-cli 官網的 public 部分的解釋。
目前第二個回調參數的使用場景不是很明確(其實不用回調也能夠,外層關閉對應的 log 便可)
我嘗試過本身新建立一個 webpack-chain 而後返回(源碼上好像會對返回值進行判斷,若是有那麼會調用 merge 方法),按理來講返回一個 config.toConfig() 應該 沒問題,可是行不通,目前仍然用的是 push 方法
--by kazehaiya