Appearance
模板通信设计
模板通信设计
接着上个章节所说的,我们需要实现可以告诉编辑器有哪些 组件
以及组件的 schema
信息,那么这一章节我们将会详细的介绍如何实现。首先我们先来脑暴一下,编辑器希望我们告诉他哪些有用的信息?
- 有哪些组件:编辑器需要对用户展示当前模板有哪些可用的模板组件
- 组件的基础信息:组件缩略图,用于展示给用户基础的 UI 样式。组件的名称,用于模板的编排。组件的描述,用于提示用户该组件的一些信息。组件的可编辑内容,用于用户对组件信息编写
- 如何告知:毕竟组件和编辑器是解耦的,那应该通过什么方式来通知编辑器?
有哪些组件
告诉编辑器我们的模板有哪些组件,需要解决的第一个问题是有哪些组件?按照我们之前的约定,所有的组件我们都写到了 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",
}
]
如何告知
上面我们已经完成了对模板组件和相关信息的获取,那么接下来我们需要把我们的这些信息告知给编辑器,这里就涉及到模板页面和编辑器之间的数据通信问题。要解决这个问题,我们可以考虑一下我们的模板将会以什么形式展示在编辑器中:
- 获取模板的基础信息,然后自己根据模板信息,再编辑器内画一个模板。
- 每次发布生成模板缩略图,展示的是图片。
- 通过 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
端存储,由于篇幅的关系,这里不再详细赘述。
接下来我们再思考一个问题:可视化编辑会对组件进行增删改的动作,那么模板如何来动态感知增删改呢?