前言
如果你正在使用Vue
框架,那麼你肯定知道Vue CLI
是什麼。Vue-cli 3
,它是Vue.js 開發的標準工具(腳手架),提供專案支架和原型設計。
除了日常構建打包專案,Vue CLI3
的一個重要部分是cli-plugins
,外掛開發。
本文將教你如何科學的建立一個Vue-CLI 外掛
,以及專案獨立npm
包。
1. 什麼是CLI plugin
它可以修改內部webpack
配置並將命令注入到vue-cli-service
。一個很好的例子是@vue/cli-plugin-typescript
:當你呼叫它時,它會tsconfig.json
為你的專案新增一個並更改App.vue
型別,整個過程不需要手動執行。
外掛非常有用,但有很多不同的情況: Electron
構建器,新增UI
庫,如iview
或ElementUI
....如果你想為某個特定的庫提供一個外掛但卻不存在呢?這時候,構建一個屬於自己專案的外掛就是個不錯的選擇。
在本文中,我們將構建一個vue-cli-plugin-rx
。它允許我們向專案新增vue-rx
庫,並在我們的Vue應用程式中獲得RxJS
支援。
2. Vue-cli外掛目錄結構
CLI
外掛是一個可以為 @vue/cli
專案新增額外特性的 npm
包。它應該始終包含:
- 一個
Service
外掛作為其主要匯出 - 可選的包含一個
Generator
和一個Prompt
檔案。
.
├── README.md
├── generator.js # generator (可選)
├── prompts.js # prompt 檔案 (可選)
├── index.js # service 外掛
└── package.json
複製程式碼
如果你需要在外掛安裝的同時,通過命令列來選擇是否建立一些示例元件,那麼目錄可以改為:
.
├── README.md
├── generator
│ └── index.js # generator
├── prompts.js # 命令列提示安裝
├── index.js # service 外掛
└── package.json
複製程式碼
2.1 GeneratorAPI
一個釋出為 npm 包的 CLI 外掛可以包含一個 generator.js
或 generator/index.js
檔案。外掛內的 generator 將會在兩種場景下被呼叫:
- 在一個專案的初始化建立過程中,如果
CLI
外掛作為專案建立preset
的一部分被安裝。 - 外掛在專案建立好之後通過
vue invoke
獨立呼叫時被安裝。
GeneratorAPI
允許一個 generator
向 package.json
注入額外的依賴或欄位,並向專案中新增檔案。
2.2 Service 外掛
Service 外掛
接收兩個引數的函式:一個PluginAPI
例項和一個包含專案本地選項的物件。它可以擴充套件/修改不同環境的內部webpack
配置,併為其注入其他命令vue-cli-service
。
但在這裡,我們只想在必要時新增一些依賴項和示例元件。所以我們的index.js
長這樣:
module.exports = (api, opts) => {}
複製程式碼
如果你想改變內部webpack
配置或其它操作,請在官方Vue CLI文件中閱讀本節
2.3 Package.json
keywords
指定了在庫中搜尋時能夠被哪些關鍵字搜尋到,所以一般這個會多寫一些專案相關的詞在這裡,是一個字串的陣列。
{
"name": "vue-cli-plugin-rx",
"version": "1.0.0",
"description": "",
"main": "index.js",
"keywords": [
"vue",
"vue-cli",
"rxjs",
"vue-rx"
],
"author": "",
"license": "ISC"
}
複製程式碼
3. 通過generator新增依賴項
generator
可幫助我們新增依賴項並更改專案檔案。所以,我們需要的第一步是讓我們的外掛新增依賴項:rxjs
和vue-rx
(你也可以新增其它):
// generator/index.js
module.exports = (api, options, rootOptions) => {
api.extendPackage({
dependencies: {
'rxjs': '^6.3.3',
'vue-rx': '^6.1.0',
},
});
}
複製程式碼
generator
匯出一個接收三個引數的函式:GeneratorAPI
例項,生成器選項和 - 如果使用者使用某個預設建立專案 - 整個預設將作為第三個引數傳遞。
api.extendPackage
方法將會修改專案的package.json
。
在本文的例子中,我們將兩個依賴項新增到dependencies
。
現在我們需要更改main.js
檔案。為了使RxJS
能在Vue元件中工作,我們需要匯入VueRx
和呼叫Vue.use(VueRx)
- 首先,我們建立一個想要新增的字串到主檔案:
let rxLines = `\nimport VueRx from 'vue-rx';\n\nVue.use(VueRx);`;
複製程式碼
- 使用
api.onCreateCompletehook
。在檔案寫入磁碟時呼叫它:
api.onCreateComplete(() => {
const fs = require('fs');
const mainPath = api.resolve(''./src/main.js');
};
複製程式碼
- 現在我們修改檔案內容:
api.onCreateComplete(() => {
const fs = require('fs');
const mainPath = api.resolve('./src/main.js');
// 獲取內容
let contentMain = fs.readFileSync(mainPath, { encoding: 'utf-8' });
const lines = contentMain.split(/\r?\n/g).reverse();
// 注入import
const lastImportIndex = lines.findIndex(line => line.match(/^import/));
lines[lastImportIndex] += rxLines;
// 修改應用
contentMain = lines.reverse().join('\n');
fs.writeFileSync(mainPath, contentMain, { encoding: 'utf-8' });
});
};
複製程式碼
4. 本地測試cli-plugin
首先我們建立一個簡單的Vue-cli專案:
vue create test-app
複製程式碼
cd到專案資料夾並安裝我們新建立的外掛:
cd test-app
npm install --save-dev file://Users/hiro/練習/測試/vue-plugin
複製程式碼
安裝外掛後,需要呼叫它:
vue invoke vue-cli-plugin-rx
複製程式碼
現在,你檢視test-app
專案的main.js
,將會看到:
import Vue from 'vue'
import App from './App.vue'
import VueRx from 'vue-rx';
Vue.use(VueRx);
複製程式碼
同時,檢視package.json
將會發現:
"dependencies": {
"core-js": "^2.6.5",
"rxjs": "^6.3.3",
"vue": "^2.6.10",
"vue-router": "^3.0.3",
"vue-rx": "^6.1.0",
"vuex": "^3.0.1"
}
複製程式碼
5. 通過generator建立示例元件
經過上面的驗證,外掛已有效。此時,我們可以擴充套件一下它的功能,建立示例元件,方便其他人理解和使用。
5.1 編寫示例元件
我們建立的這個示例元件。它應該是位於專案src/components
資料夾中的檔案。
於是我們可以在generator
目錄下,建立/template/src/components
:
這一個簡單的RxJS
驅動的計數器,帶有兩個按鈕
<template>
<section>
<h1>Click on 'Count' button to count your clicks</h1>
<button v-stream:click="count$">Count clicks</button>
<button @click="clearCounter">Clear counter</button>
<p>{{result$}}</p>
</section>
</template>
<script>
import {
filter,
bufferWhen,
debounceTime,
map,
startWith,
} from 'rxjs/operators';
export default {
domStreams: ['count$'],
subscriptions() {
return {
result$: this.count$.pipe(
filter(event => !!event),
bufferWhen(() => this.count$.pipe(debounceTime(400))),
map(clicks => clicks.length),
startWith(0),
),
};
},
methods: {
clearCounter() {
this.count$.next(null);
},
},
};
</script>
<style>
button {
padding: 10px;
font-size: 14px;
margin-right: 10px;
border-radius: 4px;
outline: none;
}
</style>
複製程式碼
不需要關心RxJS
做了什麼(反正我也沒看懂),引就vans
了。
此時我們需要改動generator/index.js
,使它可以識別並寫入資料夾。
api.render('./template', {
...options,
});
複製程式碼
當你呼叫 api.render('./template')
時,generator
將會使用 EJS
渲染 ./template
中的檔案 (相對於 generator
中的檔案路徑進行解析)
5.2 命令列提示安裝
如果使用者是個老手,不想擁有示例元件,該怎麼辦?在外掛安裝過程中,我們可以向prompts.js
新增提示程式碼,以供使用者在命令列選擇:
module.exports = [
{
name: `addExample`,
type: 'confirm',
message: '是否新增示例元件到專案components目錄?',
default: false,
},
];
複製程式碼
詢問使用者是否要將示例元件新增到專案components
目錄下。預設是:false
。
這時我們需要修改下generator/index.js
:
if (options.addExample) {
api.render('./template', {
...options,
});
}
複製程式碼
此時我們撤回安裝,重新執行
yarn add --save-dev file://Users/hiro/練習/測試/vue-plugin
vue invoke vue-cli-plugin-rx
複製程式碼
將會看到:
此時你檢視專案components
目錄,將會發現多了示例元件檔案
6.如何釋出外掛
來自官方文件
為了讓一個 CLI
外掛能夠被其它開發者使用,你必須遵循 vue-cli-plugin-<name>
的命名約定將其釋出到 npm 上。外掛遵循命名約定之後就可以:
- 被
@vue/cli-service
發現; - 被其他開發者搜尋到;
- 通過
vue add <name>
或vue invoke <name>
安裝下來。
你只需要在package.json
中新增描述description
,以及在外掛專案根目錄下建立logo.png
。
接下來就是註冊npmjs.com
2、設定倉庫地址為npm官方倉庫地址(國內大部分都使用阿里淘寶映象,如果沒改publish會失敗)
npm config set registry https://registry.npmjs.org/
3、登陸npm,使用者名稱密碼郵箱需要全部匹配
npm whoami
npm login
Username: xxxxx
Password:
Email: (this IS public) xxx@gmail.com
Logged in as xxxxx on https://registry.npmjs.org/.
4、登陸完可以publish了,執行以下命令
cd dist && npm publish && cd ../
或npm publish dist
輸出以下資訊說明發布成功
+ ngx-xxx@0.0.1
這時登入https://www.npmjs.com/可以看到自己釋出的專案
複製程式碼
完事。
總結
Vue-CLI
外掛開發,對於很多專案,當你需要引入一些自己以前編寫過的元件或功能,卻不想復刻一遍main.js
和Package.json
,學會了這招,開發賊快。當有人問你如何組織專案的元件庫時,嘖嘖...你說你都是安裝自己寫的外掛。
求一份深圳的內推
好了,又水完一篇,入正題:
目前本人在(又)準備跳槽,希望各位大佬和HR小姐姐可以內推一份靠譜的深圳前端崗位!996.ICU
就算了。
- 微信:
huab119
- 郵箱:
454274033@qq.com
作者掘金文章總集
- 「從原始碼中學習」面試官都不知道的Vue題目答案
- 「從原始碼中學習」Vue原始碼中的JS騷操作
- 「從原始碼中學習」徹底理解Vue選項Props
- 「Vue實踐」專案升級vue-cli3的正確姿勢
- 為何你始終理解不了JavaScript作用域鏈?