Skip to content
On this page

模板通信设计


模板通信设计

接着上个章节所说的,我们需要实现可以告诉编辑器有哪些 组件 以及组件的 schema 信息,那么这一章节我们将会详细的介绍如何实现。首先我们先来脑暴一下,编辑器希望我们告诉他哪些有用的信息?

  1. 有哪些组件:编辑器需要对用户展示当前模板有哪些可用的模板组件
  2. 组件的基础信息:组件缩略图,用于展示给用户基础的 UI 样式。组件的名称,用于模板的编排。组件的描述,用于提示用户该组件的一些信息。组件的可编辑内容,用于用户对组件信息编写
  3. 如何告知:毕竟组件和编辑器是解耦的,那应该通过什么方式来通知编辑器?

有哪些组件

告诉编辑器我们的模板有哪些组件,需要解决的第一个问题是有哪些组件?按照我们之前的约定,所有的组件我们都写到了 components 目录下。那么无非是读取 components 目录中的组件信息,进行展示即可。在前端获取目录的组织结构,我们可以利用 webpack 提供的 require.context 的功能,比如(后面再介绍一下其他的实现方式):

javascript
function getComponent() {
  const componentConfig = [];
  const requireConfig = require.context(
    './components',
    // 是否查询其子目录
    true,
    /package.json$/
  );
  requireConfig.keys().forEach(fileName => {
    const config = requireConfig(fileName);
    componentConfig.push(config);
  });

  return componentConfig;
}

这里需要注意的是,我们的组件下都有一个 package.json 文件,来描述该组件信息,所以我们只需要根据 package.json 的信息就能感知到组件的信息。

组件的基础信息

到这里我们已经得到了当前模板有多少个组件。接下来我们就需要对组件的基础信息进行设计,目前可以把我们的基础信息全部放到当前组件目录的 package.json 文件中,这样可以方便我们再写组件时,快速完善组件信息。拿 banner 组件举例,按照 组件名组件描述组件缩略图schema 的方式设计以下数据结构:

json
{
  "name": "coco-banner",
  "description": "banner 组件",
  "snapshot": "https://cdn.img/banner.png",
  "schema": {
    "type": "object",
    "properties": {
      "src": {
        "title": "图片地址",
        "type": "string",
        "format": "image"
      },
      "link": {
        "title": "跳转链接",
        "type": "string",
        "format": "url"
      }
    },
    "required": [
      "src"
    ]
  }
}

再结合之前的获取组件信息的代码,我们执行后,对编辑器展示出的模板所有组件信息,就可以用以下数据结构来表达:

json
[
  {
    description: "banner 组件",
    name: "coco-banner",
    schema: {...},
    snapshot: "https://cdn.img/banner.png",
  },
  {
    description: "form 组件",
    name: "coco-form",
    schema: {...},
    snapshot: "https://cdn.img/form.png",
  }
]

如何告知

上面我们已经完成了对模板组件和相关信息的获取,那么接下来我们需要把我们的这些信息告知给编辑器,这里就涉及到模板页面和编辑器之间的数据通信问题。要解决这个问题,我们可以考虑一下我们的模板将会以什么形式展示在编辑器中:

  1. 获取模板的基础信息,然后自己根据模板信息,再编辑器内画一个模板。
  2. 每次发布生成模板缩略图,展示的是图片。
  3. 通过 iframe 的方式直接把模板页面内嵌入编辑器。

方案1 实现不太靠谱,因为前面已经说了,我们的模板与编辑器已经解耦,自己实现可能需要考虑到模板用到了哪些框架,哪些组件,不太现实。 方案2 也不太靠谱,因为缩略图一方面是没法对交互进行展示,比如点击按钮要展示弹窗,如果是缩略图的化,无法处理交互。其次缩略图怎么更新,如何更新也是一个问题。不能保证缩略图就是最终配置的效果。

总之因为模板和编辑器后台已经解耦,所以要想模板可以在编辑器后台展示,那么最常用、轻便的办法就是通过 iframe 的形式内嵌模板页面。那么通信问题就转化为如何实现 2 个 iframe 之间的通信。所以可以采用 postMessage:

javascript
export function postMsgToParent (message) {
  window.parent.postMessage(
    message,
    '*'
  );
}

// 通知父容器
postMsgToParent({
  type: 'returnConfig',
  data: {
    components: this.componentConfig, // 当前模板信息
    // ...
  }
});

总结

除了通过 require.context 方式我们也可以通过 server 层存储,但是需要注意的是,每次新建页面的时候,都需要基于模板 fork 出来一份数据。因为需要将页面和模板数据进行解耦,避免模板对页面的影响。require.context 便是其中的一种实现方式,只不过把原本存于 server 端的数据变相存储到了页面中。有兴趣的同学可以自行尝试 server 端存储,由于篇幅的关系,这里不再详细赘述。

接下来我们再思考一个问题:可视化编辑会对组件进行增删改的动作,那么模板如何来动态感知增删改呢?