vue-cli 3.x带来了ui控制台的体验,让不熟悉cli命令的开发者可以更快的上手,同时提供了强大的插件拓展机制,能够以项目纬度定制本身的ui命令。近期也准备为架构方案提供ui的可视化配置,对vue-cli中ui的设计逻辑进行了必定的了解,特此记录,欢迎你们进行斧正html
总体架构图搬运自vue-cl官网 前端
运行vue ui
能够启动本地server,server相关参数和服务端启动细节,不是本文讨论范围,就不做赘述了,启动代码详见:vue
github.com/vuejs/vue-c… github.com/Akryum/vue-…node
上述架构中承担相当重要的部分就是plugin,就是由于它暴露的API容许了开发者能够经过加强项目的配置和任务,也能够分享数据和进程间的通讯,下面将从一个进入项目开始解析,插件是如何加载及应用。webpack
源码目录:cli-ui/apollo-server/connectors/project.jsgit
vue-cli ui是以项目为纬度进行管理的,在建立/导入项目并进行相应项目后,将获取项目相关的信息(path),并存储项目信息用做下次默认打开github
// load plugins
...
// Save for next time
context.db.set('config.lastOpenProject', id).write()
复制代码
源码目录:cli-ui/apollo-server/connectors/plugins.jsweb
获取一个project的path信息后,将获取项目相关的pluginsvue-cli
// Load plugins
await plugins.list(project.path, context)
复制代码
ui插件主要从三个地方获取:express
这里能够经过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做为特殊插件优先加载
// 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进行拓展
客户端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
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 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…