项目背景
电子相册,一个可以可视化编辑的H5网页,用户只需要选择相应的模板进行组合,替换页面里的素材就能制作出一套可以媲美专业人员进行开发的H5宣传页。
使用者是没有任何开发经验的用户,这种情况对系统的可用性和拓展性的要求非常的高,因为每个人的想法是不一样的,这样就导致系统对接的需求是多变的,系统必须要有非常高的可拓展性,才能非常灵活的适应用户的需求。所以我们应该怎样去设计这个系统呢?用玩具做一个比喻,系统的用户可以用孩子来比喻,每个孩子对玩具的喜好不一样,我们不能提供一个做好的玩具给所有的孩子,而是应该提供一些模块给他们让他们自由发挥,组装自己喜好的玩具;所以系统的设计应该像乐高积木一样,供用户自由的去搭建和组装自己喜欢的玩具,而不是做一个现成的玩具给用户。
编辑器的架构
系统的核心流程图
思路分析:用户通过编辑器将编辑好的数据存到服务端,C端的浏览页面只需要一个容器,可以理解为一个div,在加载页面的时候,调用后端接口异步的去获取页面配置,页面配置单纯的就是个Json字符串。配置数据取出来之后,渲染引擎开始解析Json,最后再通过Vue组件的Render方法渲染页面。
思路很清晰了,但如何实现呢,我提炼出了几个核心的问题。
- Json格式如何定义?
- Json如何和组件对应起来?
- 组件是怎么渲染出来的?
- 组件间如何通信?
基础组件
组件是页面里最小的单位,比如一个按钮组件,有不同的颜色,大小等等可以进行配置的属性。
// button.js
export default {
name: 'button',
render() {
const {
color,
textAlign,
backgroundColor,
fontSize,
lineHeight,
borderColor,
borderRadius,
borderWidth,
text
} = this;
const style = {
color,
textAlign,
backgroundColor,
fontSize: fontSize,
lineHeight: lineHeight + 'em',
borderColor,
borderRadius: borderRadius + 'px',
borderWidth: borderWidth + 'px',
textDecoration: 'none'
};
return (
<button style={style}>
{text}
</button>
)
},
props: {
text: {
type: String,
default: ''
},
vertical: {
type: Boolean,
default: false
},
backgroundColor: {
type: String,
default: "#ffffff"
},
color: {
type: String,
default: "#ffffff"
},
fontSize: {
type: Number,
default: 14
},
lineHeight: {
type: Number,
default: 1
},
borderWidth: {
type: Number,
default: 1
},
borderRadius: {
type: Number,
default: 4
},
borderColor: {
type: String,
default: "#ffffff"
},
textAlign: {
type: String,
default: "center"
}
}
}
将这些属性抽离出来后就形成了系统里最小的数据单元。
数据组装和分类
俗话说的好,人不可能一口吃成胖子,数据的组装也是,需要分步骤,分类别地来组装。 这是比较复杂的一个模块,页面的交互非常的多,细节也非常的多。数据组装模块承载的主要功能是根据用户的操作输出一份H5页面的配置数据,这份配置数据最终由渲染引擎来处理。主要有三个核心的组装步骤:
- 将多个不同功能的组件组装成单个的页面(element.js)
- 将多个页面组装成一套模板(page.js)
- 将多个模板进行自由切换(template.js)
组件标准化设计
组件的组装需要有具体的规则,不能随随便便的把一堆数据组合到一起,所以我们需要先对各个组件做一个标准化的设计,用一个约定好的JSON数据格式来装载数据。
这里总共定义了三个基础的工厂类,来按照一定的规则组装数据,输出标准化的组件。 下面来说说这三个类分别的作用
element.js 元素类就是一个组件工厂,把定义好的基础组件和JSON格式的配置项放进去,它会返回一个定制的标准化的组件。
// element.js
class Element {
constructor (ele) {
this.name = ele.name;
this.uuid = ele.uuid || +new Date();
this.pluginProps = this.getPluginProps(ele);
this.commonStyle = this.getCommonStyle(ele);
this.animations = ele.animations || [];
}
...
}
export default Element;
page.js 页面类,用于将各种组件组装成页面。
// page.js
class Page {
constructor (page = {}) {
this.uuid = page.uuid || +new Date() + parseInt(Math.random() * Math.pow(10,8));
this.pageId = page.pageId;
this.elements = page.elements;
this.coverUrl = page.coverUrl || '';
}
...
}
export default Page;
work.js 作品类,所有的页面的集合,包括所有的页面、作品封面、作品名称、是否发布等信息
// work.js
class Work {
constructor (work = {}) {
this.title = work.title || '标题';
this.templateId = work.templateId;
this.description = work.description || '描述';
this.sourceType = work.sourceType;
this.pages = work.pages || [new Page()];
this.coverImageUrl = work.coverImageUrl || '';
this.coverMusicUrl = work.coverMusicUrl;
this.isPublish = work.isPublish || 0;
}
...
}
export default Work;
数据生产和组装
基础的数据结构已经设定好了,接下来就是根据用户的操作,进行数据的生产和组装了。
技术点(组件通信):
- vuex:用来同步和保存各个组件的数据。
- event bus:监听组件派发的事件,执行相应的函数。
vuex
和event bus
能简化组件间的通信,最大程度的降低组件之间的耦合。
使用vuex
还有一个好处,它可以将所有的数据聚合到一起,方便形成一个配置文件。想想看,你如果把数据放到组件内部通过props
或者event
来传递的话,会非常的松散,最后还需要花很大的力气把数据进行聚合形成配置文件。
// store
...
const state = {
...
work: { pages: [] },
editingPage: { elements: [] },
editingElement: null,
...
}
...
页面渲染引擎
页面渲染引擎是系统最核心的部分,而render函数又是渲染引擎最核心的部分,vue的render函数接收三个参数,一个是需要创建的HTML的节点的名称、组件选项对象或者一个函数,另一个是节点需要的属性即json格式的配置文件,还有一个是子集虚拟节点。
技术点:
- render 函数
// 需要编译器
new Vue({
template: '<div>{{ hi }}</div>'
});
// 不需要编译器
new Vue({
render (h) {
return h('div', this.hi)
}
});
render(h) {
return (
...
const data = {
...
class: 'element-on-edit',
props: element.getProps(),
on: {
input: ({ value, pluginName }) => {
if (pluginName === 't-text') {
element.pluginProps.text = value;
}
},
move: ({ value, pluginName }) => {
if (pluginName === 'm-image-box') {
element.pluginProps.pos = value;
}
}
}
};
{h(element.name, data)}
...
)
}
页面配置实时预览
页面的预览页和编辑页的主要区别是预览页不用响应用户的操作,只是数据的渲染
renderPreview (h, elements) {
return (
<div style={{ height: '100%', position: 'relative' }}>
{
elements.map((element) => {
return (
<div>{h(element.name, element.getPreviewData())}</div>;
)
})
}
</div>
)
}
总结
- 用工厂模式来生成标准化的json数据
- 用
vuex
和event bus
来简化组件之间的通信,聚合用户编辑过程中产生的数据,形成配置文件 - 用render函数来解析json格式的配置文件,渲染页面
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!