Skip to content
On this page

模板动态化交互


模板动态化交互

前面我们已经介绍完了模板如何通信可视化编辑器的实现。接下来这一章节,我们继续介绍模板如何接收编辑器传递过来的对模板编辑后的消息,并消息进行实时响应。

动态组件

其实按照程序的一般设计原则,任何程序设计都可以抽象成 算法 + 数据结构 的模式。要实现模板的动态渲染,我们肯定不能按照之前 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-componentcoco-component 就包含和编辑器后台的消息通信处理以及基础的模板渲染。最后我们来写的模板可能是类似于这样的格式:

html
<CocoComponent>
  <coco-banner :obj="{
    src: require('./assets/banner.jpg'),
    link: 'https://coco.com',
  }" />
  <coco-form />
</CocoComponent>

通过类似高阶组件的方式来实现对模板通用功能的抽象。其中 coco-bannercoco-form 是对模板基础功能的初始化布局。最后整体架构大致如下:

总结

本小节我们介绍了如何来设计模板的动态化交互,接下来我们再思考一个问题:我们的模板开发完成经过测试后开开心心发布了,但是后面运营说这个表单模板我需要再加个抽奖转盘,我们又重新升级了模板。但是升级后发现跟之前发布过的页面不兼容,那么我们如何来控制模板的影响面呢?接下来我们会详细介绍模板的更新策略。