vue-cli ui设计解析

前言

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插件主要从三个地方获取:

  1. 从项目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作为特殊插件优先加载

  1. 内置默认ui,位置在cli-ui/apollo-server/ui-default
  2. 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…

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值