🛬🛬🛬手把手带你集成阿里低代码引擎vue物料库
tips:对低代码比较熟悉的同学可以直接跳过前面3章,直接进入第4章节阅读
1. 需求背景
1.1 什么是低代码
每一次的项目或功能需要全部重新开发完整代码,需要一定的技术水平,称为Pro Code (专业编程)。
低代码(Low Code)指的是一种可以使用可视化方法和少量编码来快速构建应用程序的开发方式,它通过基于拖拽+配置,再加上很少的代码来设计出页面;通过配置生成的代码在不满足功能的情况下可以二次开发
关键词:面向开发者、可视化、配置化、低门槛、快捷交付
1.2 为什么使用低代码
- 适用场景:简单重复建设页面多
- 符合降本提效的要求,减少重复代码开发,将页面开发的工作交给低代码平台
- 通过低代码生成的项目,统一进行基础库的升级。埋点、检测一系列有规则的统一配置都可以用低代码生成
- 减少人工开发,测试回归、定位问题更准确
- 商业价值:速度-快速搭建页面,进行个性化定制,成本-减少开发人数的投入,安全-机器比人安全,产生的问题更少且更容易定位
2. 业界主流低代码方案
2.1 阿里低代码引擎
已开源,文档:lowcode-engine.cn/site/docs/g…
2.1.1 架构
使用的分层架构,自下而上分别是协议 - 引擎 - 生态 - 平台。
底层协议栈定义的是标准,标准的统一让上层产物的互通成为可能 ;
引擎是对协议的实现,同时通过能力的输出,向上支撑生态开放体系,提供各种生态扩展能力
生态是基于引擎核心能力上扩展出来的,比如物料、设置器、插件等,还有工具链支撑开发体系
平台基于引擎内核以及生态中的产品组合、衔接形成满足其需求的低代码平台。
2.1.2 协议
协议是一种统一的标准,可以理解为React、Vue 等 ProCode代码和低代码 json源码如何互相解析的说明,分别定义了术语、结构和行为。
术语 用于低代码开发的统一沟通,就像一种统一的语言
结构 用于定义页面结构(类似于json),区块结构、物料描述结构等;
行为 用于定义用户交互行为,比如组件拖入到画布的拖拽行为(包括拖入钩子、拖动钩子、点击/双击钩子)等
具体使用参考官方文档:lowcode-engine.cn/site/docs/g…
2.2 华为云
软硬结合方案,软指的是自定义可复用的物料,让区块个性化;硬指的是拆解低代码的基础设施,让用户DIY平台
插件定制 每个插件都是一个npm包 主题定制使用 css var变量实现
画布定制 提供相对定位能力
物料定制 包括组件、区块(提供对外配置属性)
DSL 定制 点击右上角保存按钮,会提交schema描述到后台数据库
2.3 腾讯无界
主要用于搭建pc页面(管理端低代码平台),部署支持sass和私有化部署两种方式
文档:docs.wujicode.cn/page/intro
页面搭建示例
2.4 方案对比
结合以上方案,最后采用在阿里低代码引擎上扩展的方式进行团队的前端低代码开发
- 用户群体,阿里、腾讯、华为面向的是前端开发者
- 文档支持,阿里的文档支持最全,腾讯的文档不如阿里的全面,华为没有公布对外文档
- 只有阿里的低代码引擎开源了,方便公司前端团队参考现有代码进行定制化
3. lowcode-engine功能及问题
3.1 目前开源项目支持的功能
下图为阿里低代码引擎的功能区块。绿色区域:物料面板为本文的重点
经过阿里 lowcode engine的调研和学习,我们可以知道低代码 平台是通过引擎的入料模块、编排模块、渲染模块、出码模块等四大核心模块结合强大定制和扩展能力来构建。 目前开源项目中根据构建步骤完成的功能如下 :
3.1.1 入料模块的支持
将现有的UI组件库,按照《低代码引擎物料协议规范》,书写组件物料描述文件,通过编排API(material.setAssets)注册后,低代码平台更容易使用UI组件。物料的生产和使用,需要根据引擎的渲染能力来构建,比如React渲染能力,需要React开发的UI组件库来构建物料,Vue的渲染能力,需要Vue开发的UI组件库来构建物料,其他框架实现的渲染能力,就要对应的开发框架实现的UI组件来构建物料
- React UI 组件库物料支持
目前官方支持的物料生态包括有,ant-design、fusion ui、graph-x6(图形)等物料库
- Vue UI组件库物料支持
社区内实现了Vue相关的物料demo方案,根据demo,本文需要实现ant-design-vue相关的物料库,需要实现其他组件物料可以根据本文举一反三
- 自定义物料功能的支持
自定义物料,在官方或社区提供的自定义物料框架上,自行实现需要的组件和组件相关的物料描述信息,通过构建工具生成资产信息文件给引擎的编排API进行注册,低代码平台就可以使用该物料了
通过自定义物料可以实现满足业务诉求的区块或模板
3.1.2 编排模块的支持
所谓编排,即将设计器中的所有物料,进行布局设置、组件设置、交互设置(JS 编写/逻辑编排)后,形成符合业务诉求的 schema 描述。
通过拖拽物料的方式,将物料拖入画布渲染区域,在经过生态中的编排工具(设置器、操作历史、大纲树、源码编辑等),来布局设置物料,最终生成完整的需求区块或页面
它的技术实现包括:资源加载、事件通信、响应式、外观展示 、拖拽定位机制等
具体实现可参考官方编排模块设计实现原理
3.1.3 渲染模块的支持
渲染的能力可以快速的将协议内容转化为页面内容,通过渲染能力,可以实时看到在编辑过程中的效果,还可以通过预览查看效果。
- React 相关渲染能力的支持
官方提供了 React 生态和Rax 生态渲染解决方案
- Vue相关的渲染能力的支持
社区内实现了Vue3版本的Vue-Simulator-Renderer、Vue-Renderer渲染解决方案
- 自定义渲染能力的支持
官方渲染能力中提供了适配层逻辑,将不同运行框架的差异部分,通过接口对外,让渲染层注册或适配对应所需的方法,来达到扩展渲染层的能力,通过该能力,我们只要自行实现渲染层的逻辑(比如Angular,小程序、Flutter)
3.1.4 出码模块的支持
出码的功能是将页面编辑Schema信息通过实现的出码方案转化为源码的过程
引擎框架中内置了React相关的出码方案比如: Rax,Ice.js
低代码引擎中代码生成器提供了非常灵活的插件机制,用户自行实现满足需求的出码方案,结合代码生成器生成专属出码方案
4. 实现ant-design-vue的低代码物料
4.1 组件项目初始化
npm init @knxcloud/lowcode@latest
- 初始化vue低代码组件项目
- 初始化完成后目录结构如下
your-project-name
|-- src
|-- button // button组件
|--button.vue // 可删除
|-- index.ts // 导出文件
|-- meta.ts // 核心文件,用于编写低代码定义
|-- .babelrc.js // babel配置文件
|-- vue.config.js // vue配置文件
- 修改.babelrc
支持可选链和空值操作符
module.exports = {
presets: ['@vue/cli-plugin-babel/preset'],
plugins:[
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator"
]
};
- 修改.tsconfig.json
"extends": "@vue/tsconfig/tsconfig.web.json",
"compilerOptions": {
"baseUrl": ".",
"types": ["webpack-env"]
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "typings/*.d.ts"],
"exclude": ["dist", "node_modules"]
}
4.2 组件meta定义
4.2.1 安装包
pnpm install ant-design-vue // 安装ant-design-vue包
pnpm install @ant-design/icons-vue // 安装ant-design-vue icon包
4.2.2 基础配置
meta.ts是每个文件的低代码描述定义,需要遵守阿里低代码引擎的 低代码引擎物料协议规范
基础信息
字段 | 字段描述 | 字段类型 | 是否必填 |
---|---|---|---|
componentName | 组件名称 | String | Yes |
title | 组件中文名称 | String | Yes |
description | 组件描述 | String | No |
category | 用于描述组件位于组件面板同一 tab 的哪个区域 | String | Yes |
npm | npm 源引入完整描述对象 | Object | Yes |
npm.destructuring | 是否解构 | Bool | Yes |
npm.componentName | 导出组件名 | String | Yes |
props | 属性 | Array | Yes |
props是低代码描述的核心属性,它表示了当前组件(Button)有哪些属性,定义后的属性可以在组件面板进行配置
具体有哪些属性需要参考原有的物料组件库文档,本文是Ant Design Vue的Button组件
可配置属性
4.2.3 props配置
组件属性信息
字段 | 字段描述 | 字段类型 | 是否必填 |
---|---|---|---|
name | 属性名称 | String | Yes |
propType | 属性类型 | String/Object | Yes |
title | 属性标题 | String/Object | Yes |
title.label | 属性标题,当title为Object类型时使用 | String | No |
title.tip | 属性标题提示 | String | No |
defaultValue | 属性默认值 | any | Yes |
setter | 设置器 | Array | Yes |
propType属性具体配置详见propType类型参考
setter设置器具体配置详见设置器列表
配置示例
export default {
group: 'ant-vue组件',
componentName: 'AButton',
title: '按钮',
category: '通用',
npm: {
destructuring: true,
componentName: 'AButton',
}
};
4.2.4 configure配置
configure主要用于组件的样式、事件绑定设置
编辑体验增强 configure
字段 | 字段描述 | 字段类型 | 备注 |
---|---|---|---|
supports | 通用扩展配置能力支持性 | Object | 用于通用扩展面板能力描述 |
supports.events | 支持事件列表 | Array | |
supports.loop | 支持循环设置 | Bool | 常用于列表等循环 |
supports.condition | 是否支持条件设置 | Bool | |
supports.style | 是否支持样式设置 | Bool |
配置示例
{
"configure": {
// 支持的事件枚举
"supports": {
// 支持事件列表,vue组件必须写onXXX事件绑定才能生效
"events": ["onClick", "onChange"],
// 支持循环设置
"loop": true,
// 支持条件设置
"condition": true,
// 支持样式设置
"style": true,
}
}
}
4.2.5 snippets配置
字段 | 字段描述 | 字段类型 | 是否必填 |
---|---|---|---|
snippets | 内容为组件不同状态下的低代码 schema (可以有多个),用户从组件面板拖入组件到设计器时会向页面 schema 中插入 snippets 中定义的组件低代码 schema | Object[] | Yes |
snippets.title | 物料标题 | String | Yes |
snippets.screenshot | 物料链接,在物料库中展示的图片 | String | Yes |
snippets.schema | 物料描述定义,和props字段对应 | Object | Yes |
配置示例
snippets:[
{
title: "主按钮",
screenshot:"",
schema: {
componentName: 'AButton',
props: {
style:{
marginLeft:'10px',
fontSize:'12px',
width:'66px',
height:'24px',
display:'inline-flex',
justifyContent:'center',
alignItems:'center'
},
type:"primary",
children:"主按钮",
htmlType:"button",
size:"middle",
shape:"default",
block:false
}
}
}
]
完整meta.ts配置
// 进入button下的meta.ts文件
export default {
group:"ant-vue组件",//用于描述当前组件位于组件面板的哪个 tab
componentName:"AButton", // 组件名称
title:"按钮",// 组件中文名称
category:"通用",//用于描述组件位于组件面板同一 tab 的哪个区域
npm:{ //npm 源引入完整描述对象
destructuring: true,// 是否解构
componentName: 'AButton',// 导出组件名称
},
props:[
{
title: '功能',
display: 'block',
type: 'group',
items: [
{
name: 'children',
title: {
label: '内容',
tip: 'children | 内容',
},
propType: {
type: 'oneOfType',
value: ['node', 'string'],
},
setter: ['SlotSetter', 'StringSetter', 'VariableSetter'],
},
{
name: 'htmlType',
title: {
label: '原生类型',
tip: 'htmlType | 设置 `button` 原生的 `type` 值',
},
propType: {
type: 'oneOf',
value: ['submit', 'reset', 'button'],
},
setter: [
{
componentName: 'RadioGroupSetter',
props: {
options: [
{
title: 'Submit',
value: 'submit',
},
{
title: 'Reset',
value: 'reset',
},
{
title: 'Button',
value: 'button',
},
],
},
},
'VariableSetter',
],
defaultValue: 'button',
},
{
name: 'href',
title: {
label: '跳转地址',
tip: 'href | 点击跳转的地址,指定此属性 button 的行为和 a 链接一致',
},
propType: 'string',
setter: ['StringSetter', 'VariableSetter'],
},
{
name: 'target',
title: {
label: 'Target',
tip: 'target | 相当于 a 链接的 target 属性,href 存在时生效',
},
propType: {
type: 'oneOf',
value: ['_self', '_blank', '_parent', '_top'],
},
setter: [
{
componentName: 'SelectSetter',
props: {
options: [
{
title: '本窗口跳转',
value: '_self',
},
{
title: '打开新标签页',
value: '_blank',
},
{
title: '父窗口跳转',
value: '_parent',
},
{
title: '顶层窗口跳转',
value: '_top',
},
],
},
},
'StringSetter',
'VariableSetter',
],
condition: {
type: 'JSFunction',
value: 'target => !!target.getProps().getPropValue("href")?.trim()',
},
},
],
},
{
title: '外观',
display: 'block',
type: 'group',
items: [
{
name: 'type',
title: { label: '类型', tip: 'type | 设置按钮类型' },
propType: {
type: 'oneOf',
value: ['primary', 'ghost', 'dashed', 'danger', 'link', 'text'],
},
setter: [
{
componentName: 'SelectSetter',
props: {
options: [
{
title: '主按钮',
value: 'primary',
},
{
title: '虚线框按钮',
value: 'dashed',
},
{
title: '危险按钮',
value: 'danger',
},
{
title: '链接按钮',
value: 'link',
},
{
title: '类文本按钮',
value: 'text',
},
],
},
},
'VariableSetter',
],
},
{
name: 'size',
title: { label: '尺寸', tip: 'size | 设置按钮大小' },
propType: { type: 'oneOf', value: ['large', 'middle', 'small'] },
setter: [
{
componentName: 'RadioGroupSetter',
props: {
options: [
{
title: '大',
value: 'large',
},
{
title: '中',
value: 'middle',
},
{
title: '小',
value: 'small',
},
],
},
},
'VariableSetter',
],
defaultValue: 'middle',
},
{
name: 'shape',
title: {
label: '形状',
tip: 'shape | 设置按钮形状,可选值为 `circle`、 `round` 或者不设',
},
propType: { type: 'oneOf', value: ['default', 'circle', 'round'] },
defaultValue: 'default',
setter: [
{
componentName: 'RadioGroupSetter',
props: {
options: [
{
title: '默认',
value: 'default',
},
{
title: '圆形',
value: 'circle',
},
{
title: '圆角',
value: 'round',
},
],
},
},
'VariableSetter',
],
},
{
name: 'icon',
title: { label: '图标', tip: 'icon | 设置按钮的图标组件' },
propType: 'node',
setter: {
componentName: 'SlotSetter',
initialValue: {
type: 'JSSlot',
value: [
// {
// componentName: 'AIcon',
// props: {
// type: 'SmileOutlined',
// size: 20,
// rotate: 0,
// spin: false,
// }
// }
]
}
}
},
{
name: 'block',
title: {
label: '自适应',
tip: 'block | 将按钮宽度调整为其父宽度的选项',
},
propType: 'bool',
setter: 'BoolSetter',
defaultValue: false,
},
{
name: 'danger',
title: { label: '危险按钮', tip: 'danger | 设置危险按钮' },
propType: 'bool',
setter: 'BoolSetter',
defaultValue: false,
},
{
name: 'ghost',
title: { label: '幽灵属性', tip: 'ghost | 幽灵属性,使按钮背景透明' },
propType: 'bool',
setter: 'BoolSetter',
defaultValue: false,
},
],
},
{
title: '状态',
display: 'block',
type: 'group',
items: [
{
name: 'loading',
title: { label: '载入状态', tip: 'loading | 设置按钮载入状态' },
propType: 'bool',
setter: ['BoolSetter', 'VariableSetter'],
},
{
name: 'disabled',
title: { label: '是否禁用', tip: 'disabled | 是否为禁用状态' },
propType: 'bool',
setter: ['BoolSetter', 'VariableSetter'],
defaultValue: false,
},
],
},
{
name: 'onClick',
title: { label: '点击回调', tip: '点击按钮时的回调' },
propType: 'func',
},
]
}
4.3 组件导出定义
- 进入Button组件的index.ts
import 'ant-design-vue/es/button/style';// 引入Button样式(不引入样式会有问题)
export { Button as AButton } from 'ant-design-vue';// 导出Button低代码描述
- 在源文件导出Button
// src/index.ts
export * from "./button";//所有定义的低代码组件需要导出才能访问
4.4 发布npm包
- 修改package.json文件
修改version字段,需要注意:发布npm包后该版本被锁定,需要提升版本才可发布新版本号
{
"name": "Your Project Name",
"version": "0.1.0",
"description": "Ant design vue for lowcode",
"main": "./dist/index.min.js",
"module": "./dist/lowcode-material-ant-vue.mjs",
"typings": "./dist/lowcode-material-ant-vue.d.ts",
"scripts": {
"start": "vue-cli-service lowcode:dev --config ./build.lowcode.js",
"build": "vue-cli-service lowcode:build"
}
}
- 根目录添加build.lowcode.js文件
module.exports = {
alias: {
'@': './src',//别名
},
plugins: [ // 定义需要引入的第三方库
[
"@alifd/build-plugin-lowcode",
{
engineScope: '@alilc',
buildInAssets:[
{
package: 'dayjs',
version: '1.11.7',
urls: ['https://cdn.jsdelivr.net/npm/dayjs@1.11.7/dayjs.min.js'],
library: 'dayjs',
},
{
package: 'lodash',
library: '_',
urls: ['https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js'],
},
{
package: 'iconfont-icons',
urls: '//at.alicdn.com/t/font_2369445_ukrtsovd92r.js',
},
{
package: '@ant-design/icons',
version: '4.7.0',
urls: [`//g.alicdn.com/code/npm/@ali/ant-design-icons-cdn/4.5.0/index.umd.min.js`],
library: 'icons',
},
{
package:"@ant-design/icons-vue",
version:"6.1.0",
urls: ['https://cdn.jsdelivr.net/npm/@ant-design/icons-vue@6.0.1/lib/index.js'],
library: 'icons',
},
{
package:"ant-design-vue",
version:"3.2.19",
urls:["https://cdn.jsdelivr.net/npm/ant-design-vue@3.2.19/lib/index.js"],
library:"ant-design-vue"
}
]
}
]
],
};
- 打包
pnpm run build
- 发布npm
npm发布用法不是本文重点,不熟悉的参考一分钟教你发布npm包
npm publish
见到下图,发布成功
npm官网搜索对应包名
版本号与package.json的version一致,大功告成!
4.5 项目引入
- 克隆低代码demo项目(适配vue3)
git clone https://github.com/lyllovelemon/lowcode-vue-demo.git
cd lowcode-vue-demo
pnpm install
目录结构如下
|-- lowcode-vue-demo
|-- public //公共文件目录
|-- js // 编辑器、vue3渲染对应js
|-- material // 物料库文件
|-- mock// json
|-- package_assets//包含一些第三方引入库的js文件
|-- index.html //低代码项目的首页
|-- preview.html //预览页
|--src //源代码目录
|-- assets //所有schema文件
|-- components // 自定义组件
|-- iconSetter //自定义的icon设置器,用于vue3物料
|-- Page.vue // VueRenderer渲染页面
|-- handler //适配器
|--datasource-axios-handler//官方请求支持jsonp、fetch,这里实现了请求引入axios
|-- plugins // 插件
|-- multi-page-plguin // 多页插件
|-- action.ts //低代码demo右上角的按钮方法注册
|-- init.ts //低代码demo的初始化逻辑
|-- registry.ts //注册逻辑
|-- setter.ts //设置器逻辑
|-- router //路由
|-- index.js //路由入口文件
|-- utils //工具库
|-- store.ts //本地缓存方法
|-- vue
|-- preview.ts //预览逻辑
- 引入低代码物料库
查看src/assets.json文件
packages.urls支持两种引入方式:
- 内部引入
本文示例使用内部引入,方便在公司内网中使用ant-design-vue低代码物料库
- 进入低代码物料库仓库,执行命令
pnpm run build
-
如下图,dist文件夹内容替换lowcode-vue-demo/public/material内容
-
替换成功后,修改src/assets/assets.json文件
{
"version": "0.1.0",//项目版本
"packages": [
{
"package": "lowcode-material-ant-vue",//物料库的名称,需要和npm的保持一致
"version": "0.1.1",// 最新发布的npm包版本
"library": "LowcodeMaterialAntVue", // 库名
"urls": ["/material/index.css", "/material/index.js"] // 引入的物料库css和js文件路径
}
],
"components": [
{
"exportName": "LowcodeMaterialAntVueMeta",
"url": "/material/meta.js",
"package": {
"npm": "lowcode-material-ant-vue"
}
}
],
"sort": {
"groupList": [],
"categoryList": []
},
"utils": [
]
}
- 外部引入
- packages.urls替换为npm包对应路径下的urls,components.url也替换为npm包对应路径下的url
{
"version": "0.1.0",//项目版本
"packages": [
{
"package": "lowcode-material-ant-vue",//物料库的名称,需要和npm的保持一致
"version": "0.1.1",// 最新发布的npm包版本
"library": "LowcodeMaterialAntVue", // 库名
"urls": ["https://unpkg.com\\lowcode-material-ant-vue@0.1.0\\dist\\index.css", "https://unpkg.com\\lowcode-material-ant-vue@0.1.0\\dist\\index.js"] // 引入的物料库css和js文件路径
}
],
"components": [
{
"exportName": "LowcodeMaterialAntVueMeta",
"url": "https://unpkg.com\\lowcode-material-ant-vue@0.1.0\\dist\\meta.js",
"package": {
"npm": "lowcode-material-ant-vue"
}
}
],
"sort": {
"groupList": [],
"categoryList": []
},
"utils": [
]
}
- 启动lowcode-demo-vue
pnpm run start
访问localhost:5557,如下图,组件可以正常展示
拖动组件到页面,成功!
5. 常见问题
5.1 如何定义icon组件
icon组件和其他组件不同,需要自己实现一个图标设置器,效果如下
- ant-design-vue的icon组件 API如下图,props属性定义需要和它对应
2. icon定义部分:参考阿里官方react物料库 icon的写法 lowcode-materials或者我的ant-design-vue icon的写法(仓库地址在文章末尾) 3. icon设置器部分参考代码 github.com/lyllovelemo…
觉得有帮助的可以点个star:
参考文档
转载自:https://juejin.cn/post/7249586143011962936