Appearance
模板动态化交互
模板动态化交互
前面我们已经介绍完了模板如何通信可视化编辑器的实现。接下来这一章节,我们继续介绍模板如何接收编辑器传递过来的对模板编辑后的消息,并消息进行实时响应。
动态组件
其实按照程序的一般设计原则,任何程序设计都可以抽象成 算法 + 数据结构
的模式。要实现模板的动态渲染,我们肯定不能按照之前 Vue 的开发方式写死组件:
html
<template>
<div>
<coco-banner />
<coco-form />
</div>
</template>
这样写死就无法对模板组件的顺序、格式进行编辑。要对模板的顺序进行编排,就需要我们将展示的数据结构设计出 数组
, 数组中包含了对模板可展示数据的基础描述。所以我们可以按照这样的数据格式来表述我们模板渲染的布局:
json
{
"userSelectComponents": [
{
"name": "co-banner",
"props": {
"src": "",
"link": ""
}
},
{
"name": "co-form",
"props": {
"btnText": "",
"action": ""
}
}
]
}
有了上面的数据接口,接下来我们就需要对这样的数据结构进行可视化渲染。考虑一下,我们是有一个机制来实现组件的动态渲染?对于 React
来说,由于本身就是一系列函数,所以实现起来很简单:
javascript
const Banner = (props) => {
render() {
return <div />
}
};
const Form = (props) => {
render() {
return <div />
}
}
const Components = {
'co-banner': Banner,
'co-form': Form,
};
const Page = () => {
render() {
return (
<div>
{
userSelectComponents.map(config => {
const Component = Components[config.name];
return <Component {...config.props} />;
});
}
</div>
)
}
}
对于 Vue
我们也可以使用类似于 JSX
的模式来进行渲染,但这就要我们的模板需要使用 JSX
模式来编写。但是考虑到我们应该对业务无侵入,所以我们就可以直接利用 vue 提供的 动态组件 来进行页面的布局渲染:
html
<div
:id="`coco-render-id-_component_${index}`"
:key="index"
v-for="(component, index) in components"
>
<div
:is="component.name"
:key="component + index"
:obj="component.props"
:config="component.config"
/>
</div>
这样,每当我们更改编辑器数据结构,模板就会根据新的数据结构来重新渲染布局和页面,便完成了 数据 \-> 页面
的映射关系。
接收消息
上篇我们说到了模板如何发消息给编辑器,那么编辑器对数据结构进行编辑后,如何通知模板做对应的改变呢?我们也可以通过 postMessage
来进行通信,但是具体需要调用哪个模板里面哪个功能(编辑、排序、删除),我们可以采用一种取巧的方式来写:
javascript
export default {
created() {
window.addEventListener('message', (e) => {
// 不接受消息源来自于当前窗口的消息
if (e.source === window || e.data === 'loaded') {
return;
}
this[e.data.type](e.data.data);
});
},
methods: {
addComponent() {
// todo add componet
},
changeProps(payload) {
this.$set(this.components[this.currentIndex], 'props', payload);
},
}
}
这样我们便可以通过postMessage
里面携带的 type
来动态调用相关函数,由函数体来实现动态编辑的效果。
说到这里我们再来重提一下跨框架的问题,这里的消息通信是基于 postMessage
来实现的,要想跨框架,就必须要求我们对编辑器传递过来的消息进行消费,所以我们可以设计一套通用的消息处理 Adapter
来对消息进行处理。比如就叫他 coco-component
。coco-component
就包含和编辑器后台的消息通信处理以及基础的模板渲染。最后我们来写的模板可能是类似于这样的格式:
html
<CocoComponent>
<coco-banner :obj="{
src: require('./assets/banner.jpg'),
link: 'https://coco.com',
}" />
<coco-form />
</CocoComponent>
通过类似高阶组件的方式来实现对模板通用功能的抽象。其中 coco-banner
和 coco-form
是对模板基础功能的初始化布局。最后整体架构大致如下:
总结
本小节我们介绍了如何来设计模板的动态化交互,接下来我们再思考一个问题:我们的模板开发完成经过测试后开开心心发布了,但是后面运营说这个表单模板我需要再加个抽奖转盘,我们又重新升级了模板。但是升级后发现跟之前发布过的页面不兼容,那么我们如何来控制模板的影响面呢?接下来我们会详细介绍模板的更新策略。