vue-designer


2019/12/26 vue

自己的项目 vue-designer (在线预览)相关情况,记录在此以备查阅。

# 闲谈

最近一段时间根据之前做过的一个项目,自己开发了一个新的原型项目,暂且命名为 vue-designer 吧。 想想这一年多以来做过不少项目,但是真正自己主动开发的也只有这一个,无他,人懒。 想来做技术的懒到我这个程度估计也不多了,既然终于自己做了个项目,就算只是个原型,也暂且记录一下吧。

# 开始

首先,这个项目并不是有什么独特的创意或者灵感,只是对之前项目的一次全新开发,甚至可以作为其 2.0 版本。

早在两年以前,公司就有过一个前端设计器项目,主要功能是快速搭建前端页面并展示数据。 我接手之后发现虽然项目初具雏型,核心代码却仍存在一些缺陷,在经过几个月的修补之后,勉强可以符合公司需求,因此旧的架构一直沿用至今。 在此期间我曾提出彻底重构的建议(也曾进行过调研),不过由于公司对于设计器项目也处在预研阶段,没有系统化的产品设计,我也就不再坚持己见。

这次的官方重构虽然没能实施,我却有了自己尝试重构开发新项目的想法,于是在半年前,vue-designer 项目终于被我提上日程了。

# 过程

有了想法之后,实现的过程还算比较顺利。大体框架的开发前前后后大约用了两个月时间,期间穿插了一次出差。基本也实现了从创建到编辑保存的一系列功能(当然是保存在前端)。

# 未来

对这个设计器将来的发展方向,目前我还没确定。现在我只是将它作为一个调研新功能或者测试组件用的工具。如果有机会继续深入开发的话,必然需要更多的时间和精力,所以如果有看官有兴趣欢迎加入。

# 设计

上面简单的介绍了一下这个项目的来龙去脉,下面将说明一些重要的设计思路。

# 存储结构

前文曾提到,为了重构旧版设计器项目,我曾做过一些调研。当时我首先想要优化的就是编辑的网页内容保存的数据结构。旧项目使用了数组保存网页中的所有组件,通过遍历数组来实现组件的增、删、改以及动态渲染等功能。

做过一段时间的优化之后我发现:这种对数组的操作既不符合我们对于网页结构的自然认知,也会导致实际开发过程中的代码复杂度提升。于是我开始有了重新设计存储结构的想法,这一想法的来源就是网页的 dom 结构。

使用类似 dom 的树形结构 json 来存放网页内容可以同时解决上面的两个问题。首先,要添加新功能时,我们可以按照熟悉的网页 dom 思维来设计实现了。其次,开发过程中所有涉及到网页内容修改的都可以统一封装成对 json 节点的增删改查操作,大大降低了代码复杂度。

存储结构示例:

"tree": { // 树形结构
    "id": "xxx", // 组件id
	"name": "Container", // 组件名
	"ui": "Element", // 组件类型
	"children": [{ // 子组件
	    "id": "xxx",
		"name": "Main",
		"ui": "Element",
		"children": [{
	        "id": "xxx",
			"name": "Row",
			"ui": "Element",
			"attributes": { // 组件属性
				"basics": { // 基础属性
					"gutter": { // 属性名
						"value": "" // 属性值
					},
				}
			}	
		}]
	}]
}

关于渲染与树形结构的操作逻辑由于实现方式很多,这里也就不贴出来献丑了。

# 拖拽

设计器最核心的功能可以说只有一个:通过拖拽方式可视化创建网页。本项目的拖拽使用 HTML 5 拖放 实现,拖动一个组件到编辑区并释放,即可完成一次拖拽布局操作。编辑器会根据拖拽的组件内容,在编辑区动态渲染出组件。

示例代码(以 Vue 开发为例,下同):

<!-- 可供拖拽的元素,注意 draggable 属性需为 true -->
<div draggable="true"  @dragstart="dragStart">

<!-- 编辑区,需处理 drop 事件 -->
<div @dragover="dragOver" @drop="drop">

# 组件属性

组件在 vue-designer 中是组成页面的基本元素。组件的属性绑定了组件可动态渲染的各种原生属性/事件/样式。用户可通过右侧的属性栏进行各项配置,配置完成后,编辑区会根据属性的变化对组件进行动态渲染。

示例代码:

/**
 * 提交设置的属性(部分代码)
 * @param val 属性值
 */
setEvent (val) {
  // 根据属性生成新组件
  const elementNew = getElement(currentElement, attributes)
  // 更新 tree
  updateNodeById(tree, currentElement, elementId)
  jsonRender(tree)
  // 保存
  this.$store.dispatch('updateTree', { tree, currentElement: elementNew }).then()
  // 重新渲染当前组件
  ...
}

# 数据接入

数据接入功能与展示作为连接前端设计器与后台服务的重要桥梁,是非常重要的。vue-designer 采用了统一的属性模式绑定了服务接口的相关配置,用户只需在配置窗口输入需要接入的服务地址及参数等信息即可调取服务数据(前提是服务接口允许跨域请求)。

# 栅格系统

作为支持响应式开发的设计器,vue-designer 使用 element-ui Layout 布局 实现了可配置的响应式开发模式。通过组件的属性配置可以修改其在各屏幕尺寸节点的宽度。

<!-- element-ui 布局示例 -->
<el-row :gutter="10">
  <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="1"></el-col>
  <el-col :xs="4" :sm="6" :md="8" :lg="9" :xl="11"></el-col>
  <el-col :xs="4" :sm="6" :md="8" :lg="9" :xl="11"></el-col>
  <el-col :xs="8" :sm="6" :md="4" :lg="3" :xl="1"></el-col>
</el-row>

有鉴于此,打算使用设计器响应式设计功能的用户也需要了解一些响应式设计以及栅格系统的基础知识。

# 响应式预览

响应式开发不可避免的要进行实时预览, 在实现实时预览时我曾进行了一些调研,发现如果想进行真实的预览必须让媒体查询能正常工作。于是我采用了 iframe 的方案,以一个新的窗口展示编辑区,这样就可以完美地实现响应式预览。同时,由于 iframe 窗口也可随时调整其尺寸,也就顺带实现了多尺寸实时预览的功能。

示例代码:

<!-- 尺寸调整 -->
<div class="responsive-set">
  <el-slider v-model="responsiveSize" :marks="marks" :max="1980"></el-slider>
</div>
<!-- 编辑区 -->
<div class="preview-content">
  <iframe src="/vue-designer/#/pageDesignIframe" :style="{ 'width': responsiveSize + 'px' }"></iframe>
</div>

# 总结

麻雀虽小,五脏俱全。vue-designer 从其各项功能来看,可以说基本实现了可视化网页设计器所需的所有基础功能。设计实现这些功能的过程中得到的技术积累和实践经验不可谓不宝贵。由于项目还处于调研阶段,暂时不公开源码了,后续完善之后会考虑开源。

Initializing...