前言
vue-cli 3.x帶來了ui控制檯的體驗,讓不熟悉cli命令的開發者能夠更快的上手,同時提供了強大的外掛擴充機制,可以以專案緯度定製自己的ui命令。近期也準備為架構方案提供ui的視覺化配置,對vue-cli中ui的設計邏輯進行了一定的瞭解,特此記錄,歡迎大家進行斧正
整體架構
整體架構圖搬運自vue-cl官網
node端用的apollo-graphql,前端毫無疑問是vue(- -)執行vue ui
可以啟動本地server,server相關引數和服務端啟動細節,不是本文討論範圍,就不作贅述了,啟動程式碼詳見:
github.com/vuejs/vue-c… github.com/Akryum/vue-…
plugin機制
上述架構中承擔至關重要的部分就是plugin,就是因為它暴露的API允許了開發者可以通過增強專案的配置和任務,也可以分享資料和程式間的通訊,下面將從一個進入專案開始解析,外掛是如何載入及應用。
open project
原始碼目錄:cli-ui/apollo-server/connectors/project.js
vue-cli ui是以專案為緯度進行管理的,在建立/匯入專案並進行相應專案後,將獲取專案相關的資訊(path),並儲存專案資訊用作下次預設開啟
// load plugins
...
// Save for next time
context.db.set('config.lastOpenProject', id).write()
複製程式碼
load plugins
原始碼目錄:cli-ui/apollo-server/connectors/plugins.js
獲取一個project的path資訊後,將獲取專案相關的plugins
// Load plugins
await plugins.list(project.path, context)
複製程式碼
ui外掛主要從三個地方獲取:
- 從專案package.json裡獲取外掛資訊
這裡可以通過vuePlugins.resolveForm指定到其他目錄
let pkgContext = cwd.get()
// Custom package.json location
if (pkg.vuePlugins && pkg.vuePlugins.resolveFrom) {
pkgContext = path.resolve(cwd.get(), pkg.vuePlugins.resolveFrom)
pkg = folders.readPackage(pkgContext, context)
}
pkgStore.set(file, { pkgContext, pkg })
let plugins = []
plugins = plugins.concat(findPlugins(pkg.devDependencies || {}, file))
plugins = plugins.concat(findPlugins(pkg.dependencies || {}, file))
複製程式碼
findPlugins通過正則規則/^(@vue/|vue-|@[\w-]+/vue-)cli-plugin-/來過濾dependencies和devDependencies中的外掛@vue/cli-service作為特殊外掛優先載入
- 內建預設ui,位置在cli-ui/apollo-server/ui-default
- package資訊中指定的自定義ui外掛
// load custom ui pulgins
const { pkg, pkgContext } = pkgStore.get(file)
if (pkg.vuePlugins && pkg.vuePlugins.ui) {
const files = pkg.vuePlugins.ui
if (Array.isArray(files)) {
for (const file of files) {
runPluginApi(pkgContext, pluginApi, context, file)
}
}
}
複製程式碼
拿到plugins後,將開始逐個允許外掛,這裡會涉及到一個重要的api PluginApi
,它是整個外掛機制執行的核心,提供hooks供外掛新增配置、任務、檢視等,原始碼位於cli-ui/apollo-server/api/PluginAPI.js
// run plugin api
function runPluginApi(id, pluginApi, context, filename = 'ui') {
...
try{
// 核心邏輯將pluginApi作為引數傳給各個模組外掛
module(pluginApi)
} catch(e) {
}
}
複製程式碼
通過執行三類外掛,使得外掛可以通過pluginApi進行擴充
add client addons
客戶端addons通過pluginApi的addClientAddon新增
//外掛中註冊
module.exports = api => {
api.addClientAddon({
id: 'org.vue.webpack.client-addon',
path: '@vue/cli-ui-addon-webpack/dist'
})
}
複製程式碼
對應客戶端則會載入/_addon/org.vue.webpack.client-addon/index.js生產環境下
,而完成這一邏輯的主要依賴,以下幾部分的設定
// 服務端儲存新增的addon
pluginApi.clientAddons.forEach(options => {
clientAddons.add(options, context) //儲存addons資訊 並監聽變化響應介面
})
// 服務端設定,express外掛cli-ui/cli-ui/apollo-server/server.js
module.exports = app => {
...
app.use('/_addon/:id/*', clientAddons.serve)
...
}
// 外掛包打包設定,配置vue.config.js
module.exports = {
...clientAddonConfig({ // clientAddonConfig為預設開發設定,原始碼在cli-ui/index.js
id: 'org.vue.webpack.client-addon', //id作為檔案載入標識
port: 8096 // port用作開發模式下服務的埠
})
}
複製程式碼
最終客戶端通過介面載入對應的addon指令碼檔案,現在你可以在檢視中使用addon
api.describeTask({
/* ... */
// 額外的檢視 (例如 webpack dashboard)
// 預設情況下,這是展示終端輸出的 'output' 檢視
views: [
{
// 唯一的 ID
id: 'org.vue.webpack.views.dashboard',
// 按鈕文字
label: 'Dashboard',
// 按鈕圖示 (material-icons)
icon: 'dashboard',
// 載入的動態元件,會用 ClientAddonApi 進行註冊
component: 'org.vue.webpack.components.dashboard'
}
],
// 展示任務詳情時預設選擇的檢視 (預設情況下就是 output)
defaultView: 'org.vue.webpack.views.dashboard'
})
複製程式碼
而這裡的org.vue.webpack.components.dashboard其實就是對應我們載入的addon裡面註冊的vue元件
...
ClientAddonApi.component('org.vue.webpack.components.dashboard', WebpackDashboard) //ClientAddonApi通過注入到windows作為全域性變數使用,用於元件註冊和載入
...
複製程式碼
add views
// Add views
for (const view of pluginApi.views) {
await views.add({ view, project }, context)
}
複製程式碼
載入自定義檢視,這裡是指通過api.addView來新增的檢視,具體方式可以參見:cli.vuejs.org/zh/dev-guid…
這裡需要注意addView裡面的id和name均需要通過addClientAddon註冊過
register widget
// Register widgets
for (const definition of pluginApi.widgetDefs) {
await widgets.registerDefinition({ definition, project }, context)
}
複製程式碼
註冊widget,可以為專案dashboard頁面新增自定義的ui外掛,通過registerWidget來進行註冊
module.exports = api => {
const { registerWidget, onAction, setSharedData } = api.namespace('org.vue.widgets.')
registerWidget({
id: 'welcome',
title: 'org.vue.widgets.welcome.title',
description: 'org.vue.widgets.welcome.description',
icon: 'mood',
component: 'org.vue.widgets.components.welcome', //這裡的component也必須是已經在addon裡面註冊過的元件
minWidth: 3,
minHeight: 4,
maxWidth: 3,
maxHeight: 4,
maxCount: 1
})
}
複製程式碼
總結
通過各種檢視、wigdet的初始化定義,在客戶端請求資訊介面後,根據獲取到的資訊渲染對應的內容。除了外掛機制外,cli-ui還有許多地方值得借鑑,比如通過node-ipc實現程式間通訊、通過ShareData的設計實現不同server資料和客戶端資料的實時同步,後續有機會將會繼續分享
最後獻上官方cli-ui外掛開發連結:cli.vuejs.org/zh/dev-guid…