README
政务配置化引擎 - venom dsl 渲染引擎
@aligov/gov-venom-render
venom dsl 浏览器端渲染引擎,在线体验下:地址
快速上手
有两种接入方式:
- JIT:即在浏览器端对符合协议的 schema 数据(json 或者 jsx 格式,目前仅支持 jsx)进行实时的解析渲染的方式;
- AOT:通过提供 webpack loader,在云端构建的方式,将符合协议的 schema 数据转化成可以执行的 js/css 代码并发布至 cdn,然后嵌到 html 中执行渲染,即对 schema 协议数据的编译解析放到了工程侧完成。
JIT
浏览器端对 schema 数据进行编译解析及页面渲染。
对此,我们统一提供了@aligov/gov-venom-render组件,用法示例可以参考demo。
他是 dsl 的浏览器端渲染引擎。具体用法说明如下:
返回一个组件
import render from '@aligov/gov-venom-render';
import ReactDOM from 'react-dom';
const dsl = `
<div>123</div>
`; // 可以放本地,也可以从后端取回
const App = render(dsl);
ReactDOM.render(<App />, mountNode);
直接渲染进 DOM
import render from '@aligov/gov-venom-render';
const dsl = `
<div>123</div>
`; // 可以放本地,也可以从后端取回
render(dsl, {
container: '#container',
});
目前,render 中已默认内置了以下组件:
- State 组件:用于辅助声明可配置数据部分,他只有一个 model 属性,model 属性里可以放置你的数据操作相关的内容,他是一个对象,里面包含了三个固定的属性:
- state: 数据声明
- methods: 通用处理函数,目前已内置 message 方法,用于信息提示;如果放置了 init 方法在 methods 对象中,则会在页面初始化后自动执行该方法,在 UI 中通过**$store.methods.xxx()**的方式来直接调用
- actions: 数据操作处理,可以有同步操作,也可以使用有副作用的异步请求操作,异步请求操作可以使用 es7 的 async/await;每一个 actions 里的函数,会内置:state 及 payload 两个参数,state 里有上面声明的数据项,还有 set 方法来设置数据,在使用 set 设置数据后会触发 UI 渲染;actions 里的方法通过在 UI 的表达式中以**$state.dispatch('xxx', args)**的方式来触发,payload 就是触发时传递过来的参数,这一部分的实现直接内置了轻量级的数据流管理方案roy.js,当然是可替换的,比如也可以替换为 dva 之类的
- fusion next UI 组件,如:Button、Dialog、Input 等;
- 部分业务组件,如:@aligov/components-table-actions 等;
- 集团统一表单方案:Formily的表单及 UI 组件,该部分的 UI 组件不会与 next 的组件重复;
- 集团统一 search list 组件:Alist;
如果不够用,可以自己注册组件:
import React from 'react';
// 自开发组件示例
const MyComp = (props) => {
return <div>开发者自己的组件示例 {props.name}</div>;
};
// 可以本地手动维护,也可以由后端提供,还可以在平台侧维护,平台还在开发中(手动捂脸)
const dsl = `
<MyComp
name='张三'
/>
`;
render(dsl, {
components: {
MyComp,
},
container: '#container',
});
组件式用法
为了进一步简化使用,我们还提供了组件式的使用方式,如:
import { Venom } from '@aligov/gov-venom-render';
const dsl = `
<>
<State
model={{
methods: {
},
state: {
name: '点我',
count: 0
},
actions: {
async asyncFetch(state, payload) {
this.methods.fetch(state);
this.methods.setMyName('my new name');
state.set({
name: 'hello world~'
});
setTimeout(() => {
this.methods.setMyName('3s change');
}, 3000);
}
}
}}
/>
<>
<button onClick={() => {
$store.dispatch('asyncFetch');
}}>{$state.name}</button>
<div>{$state.count}</div>
<TextComp />
</>
</>
`;
const TextComp = (props) => {
return <div>text component test</div>;
};
export default (props) => {
return (
<Venom
dsl={dsl}
init={({ methods, ...others }, ...args) => {
console.log('init args:', args);
}}
components={{
TextComp,
}}
methods={{
setMyName: (myName) => {
this.setState({ myName });
},
async fetch(state, payload) {
await new Promise((resolve, reject) => {
setTimeout(() => {
state.set('count', 100);
resolve();
}, 2000);
});
},
}}
/>
);
};
当然,也支持直接传入 url 来完成渲染,并支持 format 做格式化:
import { Venom } from '@aligov/gov-venom-render';
export default (props) => {
return (
<Venom
url='https://www.fastmock.site/mock/8b5ab209e9d13691117cba3b7baea9c4/dsl/venom/dsl'
format={(res) => res}
init={({ methods, ...others }, ...args) => {
console.log('init args:', args);
}}
methods={{
setMyName: (myName) => {
this.setState({ myName });
},
async fetch(state, payload) {
await new Promise((resolve, reject) => {
setTimeout(() => {
state.set('count', 100);
resolve();
}, 2000);
});
},
}}
/>
);
};
model 的两种写法
我们把渲染引擎分为了 UI 跟 Model 两部分。model 部分既可以单独维护,也可以直接存放在 DSL 中。
单独维护
// model.ts
export default {
state: {
name: '张三',
},
};
// index.html
<div>{$state.name}</div>
// index.ts
import model from './model';
import dsl from 'raw-loader!./index.html'; // 什么后缀都可以,这里只是示例
import render from '@aligov/gov-venom-render';
render(dsl, model, {
container: '#container',
});
写在 DSL 中
也可以直接将 model 写在 DSL 中,示例如下:
// index.html
<>
<State
model={{
state: {
name: '张三',
},
}}
/>
<div>{$state.name}</div>
</>
import dsl from 'raw-loader!./index.html'; // 什么后缀都可以,这里只是示例
import render from '@aligov/gov-venom-render';
render(dsl, {
container: '#container',
});
Form 表单注册组件
表单方案我们使用的是集团统一中后台方案Formily,支持在入口方法中注册表单组件。
使用纯 DSL
纯 DSL 使用演示,适用于对 DSL 做统一管理的场景。
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import render from '@aligov/gov-venom-render';
const dsl = `
<>
<State
model={{
methods: {
init({ React, formily, components, registerComponents }) {
const { registerFormField, createControllerBox, connect } = formily;
const { Checkbox } = components;
registerFormField(
'custom-string',
connect()(props => <input {...props} value={props.value || ''} />)
);
const FormLayout = createControllerBox('controller-form-layout', props => {
return (
<div>
{props.children}
{props.schema['x-component-props']['attr']}
</div>
);
});
registerComponents({FormLayout});
}
}
}}
/>
<SchemaForm>
<FormLayout attr='hello'>
<Field type="custom-string" name="custom-string" title="Custom Field" />
</FormLayout>
</SchemaForm>
</>
`;
console.log('render:', render);
const App = render(dsl, {
components: {},
});
ReactDOM.render(<App />, mountNode);
DSL 外注册组件
DSL 外注册组件使用演示,适用于在应用中部分使用 DSL,而非对 DSL 做统一管理。
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import render, { formily, components } from '@aligov/gov-venom-render';
const dsl = `
<SchemaForm>
<FormLayout attr='hello'>
<Field type="custom-string" name="custom-string" title="Custom Field" />
</FormLayout>
</SchemaForm>
`;
const { registerFormField, createControllerBox, connect } = formily;
const { Checkbox } = components;
registerFormField(
'custom-string',
connect()((props) => <input {...props} value={props.value || ''} />)
);
const FormLayout = createControllerBox('controller-form-layout', (props) => {
console.log('props:', props);
return (
<div>
{props.children}
{props.schema['x-component-props']['attr']}
</div>
);
});
const App = render(dsl, {
components: {
FormLayout,
},
});
ReactDOM.render(<App />, mountNode);
AOT
工程侧的解析编译转换引擎,作为 webpack loader 存在,为:@aligov/gov-venom-loader。
配合 build-scripts 工程构建使用,需要单独提供 build-scripts 的插件:
// build-plugin-venom.js
module.exports = async ({ onGetWebpackConfig, context }, pluginOptions = {}) => {
onGetWebpackConfig((config) => {
config.module
.rule('venom')
.test(/\.vnm$/)
.use('@aligov/gov-venom-loader')
.loader('@aligov/gov-venom-loader');
});
};
然后记得在 build.json 中引入:
{
"plugins": [
[
"build-plugin-fusion",
{
"themePackage": "@alifd/theme-design-pro"
}
],
[
"build-plugin-moment-locales",
{
"locales": ["zh-cn"]
}
],
"@ali/build-plugin-ice-def",
// 自定义插件
[
"./build-plugin-venom.js",
{
"forceBind": true
}
]
]
}
然后就可以在项目中应用起来了,可以参考示例:gov-venom-example:
TODO
[] formily 跟 alist 的注入需要统一封装实现,而不是类似现在的写死;