# 插件与扩展点
重要提醒
- 扩展点的设计很重要,在插件化开发过程中需要严格按照规范进行设计。
- 扩展点一旦定义,项目中禁止删除或修改,仅可新增功能点。
- ⭐️⭐️⭐️⭐️⭐️ 扩展点的层级只能有一层,不可以嵌套的形式进行使用;一旦存在嵌套,其维护和变更成本将呈几何形式上升。
# 简介
应用上是否需要做插件化,首先考虑业务的定制差异,差异的部分需要用插件去实现,其他通用的部分只需要按照之前的方式在主应用中去实现。
而差异的部分与通用部分是如何连接起来,这个关联点就是我们要设置的扩展点。如下图所示:
# 命名规范
【扩展点】 一般按照业务功能模块进行语义化命名,当前应用中必须保持唯一性标识,前缀以当前应用的 服务名称(package.json 的 name)
为首,后缀必须以-epu
、-epl
或-ep
结尾,其中epu
为UI类扩展点(ExtensionPoint for UI),epl
为逻辑类扩展点(ExtensionPoint for Logics),-ep
为不限制类型扩展点(ExtensionPoint),例如:
首页页头扩展点:[服务名称]-home-header-epu
首页页脚扩展点:[服务名称]-home-footer-epu
签署前签名逻辑校验扩展点:[服务名称]-signpage-presign-checksign-epl
签署前处理扩展点:[服务名称]-signpage-presign-ep
【扩展点调用插件的函数名】 驼峰命名法,命名规范为:服务名称
+扩展点名称
+ EsExtensionPoint
,例如
首页页头扩展点:[服务名称] + HomeHeaderEpuEsExtensionPoint
首页页脚扩展点:[服务名称] + HomeFooterEpuEsExtensionPoint
签署前校验扩展点:[服务名称] + SignpagePresignEpEsExtensionPoint
【插件】
文件命名 参照项目命名规则, 以当前应用的 服务名称(package.json 的 name)
为首, 以-plugin
结尾,例:[服务名称]-home-header-plugin
。
插件目录命名 /plugins/项目(或基线)标识/插件名(或多插件集合(单插件对应多扩展点)目录)
,例:/plugins/base/[服务名称]-home-header-plugin
、/plugins/base/[服务名称]-home-header-banner-plugin
,如下所示:
--- plugins
|
|-- base // 基线目录
| |
| |-- [服务名称]-home-header-plugin // 插件目录(插件集合名)
| | |
| | |-- src
| | |
| | |-- plugin
| | | |
| | | |-- home-header-left-plugin.ts
| | | |
| | | |-- home-header-right-plugin.ts
| | |
| | |-- index.ts
| |
| |-- [服务名称]-home-footer-plugin // 插件目录
| |
| |-- src
| |
| |-- plugin
| |
| |-- index.ts
|
|-- hangzhou // 项目目录
| |
| |-- [服务名称]-home-header-plugin // 插件目录(插件集合名)
| | |
| | |-- src
| | |
| | |-- plugin
| | | |
| | | |-- home-header-left-plugin.ts
| | | |
| | | |-- home-header-right-plugin.ts
| | |
| | |-- index.ts
| |
| |-- [服务名称]-home-footer-plugin // 插件目录
| |
| |-- src
| |
| |-- plugin
| |
| |-- index.ts
|
|-- config.json
插件文件中name值 必须确保唯一性,一般按照目录命名短横线拼接页面(功能模块)-...(扩展点拆分粒度自定义)-plugin
,例:
export default (): Plugin => ({
name: 'home-header-baner-plugin', // 插件的名称,唯一标识符
...
})
插件下package.json文件中name值 必须确保唯一性,在插件name值(或插件目录名)的基础上加上@插件所属域标识/
前缀(当插件可以通过插件平台去管理时,前缀的作用就能体现出来,可以防止插件包名冲突),例:
package.json
{
"name": "@esign-gov-base/home-header-plugin"
}
{
"name": "@esign-gov-hangzhou/home-header-plugin"
}
插件中的hooks方法 单个hook方法时须与扩展点名称保持一致,如果需要自定义多个hook方法时命名自定义(需保证唯一性)。
提示
扩展点、插件的业务语义化部分命名可以保持统一,如:扩展点[服务名称]-home-header-epu
--- 插件[服务名称]-home-header-plugin
,这样可以方便开发过程中或后续项目维护时 扩展点和插件互找。
# 扩展点与插件的实现
# 案例一(以组件的方式实现功能)
若以组件方式实现功能,需保证 插件资源的
key
值,EsExtensionPoint
组件的name
参数 和 插件的hook
必须保持一致
# main.js
import { EsExtensionPoint } from '@esign/plugin-core'
Vue.use(EsExtensionPoint, {
configs,
useEventEmitter: true
})
# 插件代码
import { createAsyncWaterfall } from '@modern-js/plugin'
import Component from './components/index.tsx'
import type { Plugin, PluginAPI } from '@esign/plugin-core'
export default (): Plugin => ({
name: 'ui-plugin',
registerHook: {
customComponent: createAsyncWaterfall(), // 自定义组件 hooks
},
setup({ setAppContext, useAppContext }: PluginAPI) {
return {
// 通过自定义hook返回组件
'ui-epu': () => {
return Component
}
}
}
})
# 调用插件demo test.vue
<template>
<div>
<h1>{{transformTxt}}</h1>
<!-- 扩展点插件 -->
<EsExtensionPoint name="ui-epu" msg="Welcome to the Esign Plugin Demo" />
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
@Component({})
export default class TestView extends Vue {
async mounted() {
}
}
</script>
# 案例二(以方法形式实现功能)
# 插件代码
import { createAsyncWaterfall } from '@modern-js/plugin'
import Component from './components/index.tsx'
import type { Plugin, PluginAPI } from '@esign/plugin-core'
export default (): Plugin => ({
name: 'ui-plugin',
registerHook: {
customComponent: createAsyncWaterfall(), // 自定义组件 hooks
},
setup({ setAppContext, useAppContext }: PluginAPI) {
return {
// 方式二,通过自定义hook返回组件
'ui-epu': () => {
return Component
}
}
}
})
# 调用插件demo test.vue
<template>
<div>
<h1>{{transformTxt}}</h1>
<component :is="homePageComponentName" />
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
// pluginManager 文件夹,与src、plugins同级,作用为维护插件与扩展点的对应关系
import { homePageEpuEsExtensionPoint } from '../../pluginManager'
@Component({})
export default class TestView extends Vue {
homePageComponentName = ''
async mounted() {
// 此为插件引入方法,项目一经发布,不可删除
this.homePageComponentName = await homePageEpuEsExtensionPoint();
}
}
</script>
# pluginManager/index.js
import pluginManager, { createExtensionPointHook } from '@esign/plugin-core'
// 首页插件调用方法
// 方法名称一旦确定,不再更改
export async function homePageEpuEsExtensionPoint(name: string) {
const name = 'home-page-epu'
const path = getPluginPath(name) // 获取插件路径
// 插件所提供的hooks,只可新增,不可修改及删除
const hooks = await pluginManager({ path, hooks: { [name]: createExtensionPointHook() } })
const [ Component ] = await hooks[name](null)
Vue.component(Component.name, Component)
// 返回内容格式可自定义
return Component.name
}
# 扩展点监控
思路:在主应用编译时按照扩展点的命名规则(-ep
/-epu
/-epl
)进行全局扫描扩展点
# 方案一:插件通过EsExtensionPoint
组件的方式引入
数据项 | 描述 |
---|---|
扩展点名称 | EsExtensionPoint组件prop中的name值,同时也是config.json中plugins下对应的key,例:home-header-epu |
扩展点页面path(项目中的文件目录路径、文件名) | EsExtensionPoint组件被引用的页面文件所在路径、页面文件名等,例:/src/views/Home/index.tsx |
主应用版本号 | 主应用中设置的版本号(根据迭代版本规则设置),例:2.5.3 |
插件路径 | 插件编译后的config.json中plugins下对应的value,例:/plugins/base/home-header-plugin/index.1.0.1.js |
扫描方式:每次主应用构建前通过组件依赖关系收集的webpack插件
进行项目全局扫描, 将收集到的页面与组件关系上传服务器进行清洗,组成这样对应关系:扩展点——所在页面——插件——主应用版本号
监控方式:例如:1.0.0版本的应用上定义了一个扩展点home-header-epu
,扩展点所在页面为/src/views/Home/index.tsx
文件中,加载的插件为/plugins/base/home-header-plugin/index.1.0.1.js
,1.1.0版本在发布前也会生成新的一组对应关系与1.0.0版本进行比对,如果发现扩展点缺失、扩展点所在的页面不存在或对应的插件路径未配置时,立即做告警或终止项目构建处理(但也有例外,如果上一个版本的扩展点在当前版本上缺失且无对应的页面,不做告警或拦截处理,该情况主要是指新版本上移除了一些废弃功能的场景)。
规则:总之,一般情况下之后项目版本升级时不允许修改原有扩展点(仅允许增加新扩展点,不允许修改或删除历史版本设置的扩展点)。
# 方案二:插件与扩展点通过方法的形式实现
数据项 | 描述 |
---|---|
扩展点名称 | config.json中plugins下对应的key,例:home-header-epu |
扩展点对应插件的调用函数 | pluginManager/index.ts文件中以EsExtensionPoint结尾的方法 |
扩展点页面path(项目中的文件目录路径、文件名) | 扩展点对应插件的调用函数被调用的页面文件地址,例:/src/views/Home/index.tsx |
主应用版本号 | 主应用中设置的版本号(根据迭代版本规则设置),例:2.5.3 |
插件路径 | 插件编译后的config.json中plugins下对应的value,例:/plugins/base/home-header-plugin/index.1.0.1.js |
扫描方式:需要实现主应用全局扫描的插件调用方法被调用的页面情况(扫描除pluginManager/index.ts文件之外的文件),将收集到的数据项上传服务器,组成这样的对应关系:扩展点——所在页面——插件调用函数——插件——主应用版本号
监控方式:同方案一,当后续版本对上一个版本的一个或多个数据项有修改,即做告警或终止项目构建处理。
规则:同方案一。