别再写活动页了,快来撸一个页面生成器吧!

 {{data.context}}

可以看到我们只对外暴露了一个 props,这样做法的好处是可以统一组件对外暴露的数据,组件内部爱怎么玩怎么玩。注意,这里我们也可以引入一些第三方组件库,比如mint-ui之类的。

后台编辑的实现


在写代码前,我们先考虑一下需要实现哪些功能:

  1. 一个属性编辑区,提供给使用者编辑组件内部 props 的功能

  2. 一个组件选择区,提供使用者选择需要的组件

  3. 一个组件预览区,提供使用者拖拽排序页面预览的功能

编辑区的实现


按照顺序,我们先来实现组件的属性编辑功能。我们要考虑,一个组件暴露出哪些可配置的信息。这些可配置的信息如何同步到后台编辑区,让使用者进行编辑,一个按钮的可配置信息可能是这样:

如果把这些配置全部写在后台库里面,根据当前选择的组件加载不同的配置,维护起来会相当麻烦,而且随着组件数量的增加,也会变得臃肿,所以我们可以将这些配置存储在服务端,后台只需要根据存储的规则进行解析便可,举个例子,我们其实可以存储这样的编辑配置:

[

{

“blockName”: “按钮布局设置”,

“settings”: {

“src”: {

“type”: “input”,

“require”: true,

“label”: “按钮文案”

}

}

}

]

我们在编辑后台,通过接口请求到这些配置,便可以进行规则渲染:

/**

* 根据类型,选择创建对应的组件

* @param {VNode} vm

* @returns {any}

*/

createEditorElement (vm: VNode) {

let dom = null

switch (vm.config.type) {

case ‘align’:

dom = this.createAlignElement(vm)

break;

case ‘select’:

dom = this.createSelectElement(vm)

break;

case ‘actions’:

dom = this.createActionElement(vm)

break;

case ‘vue-editor’:

dom = this.createVueEditor(vm)

break;

default:

dom = this.createBasicElement(vm)

}

return dom

}

组件选择区


首先我们需要考虑的是,组件怎么进行注册?因为组件被用户选用的时候,我们是需要渲染该组件的,所以我们可以提供一段 node 脚本来遍历所需组件,进行组件的安装注册:

// 定义渲染模板和路径

var OUTPUT_PATH = path.join(__dirname, ‘…/packages/index.js’);

console.log(chalk.yellow(‘正在生成包引用文件…’))

var INSTALL_COMPONENT_TEMPLATE = ’    {{name}}';

var IMPORT_TEMPLATE = ‘import {{componentName}} from ‘{{name}}’’;

var MAIN_TEMPLATE = `/* Automatic generated by ‘./compiler/build-entry.js’ */

{{include}}

const components = [

{{install}}

]

const install = function(Vue) {

components.map((component) => {

Vue.component(component.name, component)

})

}

/* istanbul ignore if */

if (typeof window !== ‘undefined’ && window.Vue) {

install(window.Vue)

}

export {

install,

{{list}}

}

`;

// 渲染引用文件

var template = render(MAIN_TEMPLATE, {

include: includeComponentTemplate.join(endOfLine),

install: installTemplate.join(,${endOfLine}),

version: process.env.VERSION || require(‘…/package.json’).version,

list: listTemplate.join(,${endOfLine})

});

// 写入引用

fs.writeFileSync(OUTPUT_PATH, template);

最后渲染出来的文件大概是这样:

import WButton from ‘w-button’

const components = [

WButton

]

const install = function(Vue) {

components.map((component) => {

Vue.component(component.name, component)

})

}

/* istanbul ignore if */

if (typeof window !== ‘undefined’ && window.Vue) {

install(window.Vue)

}

export {

install,

WButton

}

这个也是组件库的通用写法,所以这里的思想就是把发布到 npm 上的组件,进行聚合,聚合成一个组件包引用,我们在后台编辑的时候,是需要全量引入的:

import * as W_UI from ‘…/…/packages’

Vue.use(W_UI)

这样,我们组件便注册完了,组件选择区,主要是提供组件的可选项,我们可以遍历组件,提供一个个 List 让用户选择,当然如果我们每个组件如果只提供一个组件名,用户可能并不知道组件长什么样,所以我们最好可以提供一下组件长什么样的缩略图。这里我们可以在组件发布的时候,也通过 node 脚本进行。这里要实现的代码比较多,我就大致说一下过程,因为也不是核心逻辑,可有可无,只能说有了体验上会好一点:

  1. 用户启用 dev-server 进行代码编写测试

  2. server 脚本使用 Chrome 工具 puppeteer,调整页面到手机端模式, 进行当前 dev-server 截图。

  3. 生成截图文件,上传到node服务,关联组件

这样,就可以在加载组件选择区的时候,为组件附上缩略图。

组件预览区


当用户在选择区选择了组件,我们需要展示在预览区域,那么我们怎么知道用户选择了哪些组件呢?总不能提前全部把组件写入渲染区域,通过v-if来判断选择吧?当然没有这么蠢,Vue 已经提供了动态组件的功能了:

:class=“[index===currentEditor ? ‘active’ : ‘’]”

:is=“select.name”

:data=“select.data”>

为什么我们不用缩略图代替真实组件?一方面生成的缩略图尺寸存在问题,另一方面,我们需要编辑的联动性,就是编辑区的编辑需要及时的反馈给用户。

额外的问题


说了这么多,貌似一切都很顺利,但是这样在实践的时候,发现了存在一个明显的问题就是:我们中间的预览区域其实就是为了尽可能模拟移动端页面效果。但是如果我们加入了一些包含类似 position: fixed 样式的组件,会发现样式上就出现了明显的问题。典型的比如Dialog Loading 等。所以我们参考了 m-ui组件库的设计,将中间预览操作容器展示为一个iframe。将iframe大小调整为375 * 667,模拟 iPhone 6 的手机端。这样就不会存在样式问题了。可是这样又出现了另一个难点,那就是左侧的编辑数据如何及时的反应到iframe中?没错,就是postMessgae,大致思路如下:

利用 vuex 做数据存储池,所有的变化,通过 postMessgae 进行同步,这样我们只用确保数据池中的数据变化,便可以映射到渲染层的变化。比如,我们在预览区进行了组件选择和拖拽排序,那么我们只需通过 vuex 出发同步信息便可:

// action.ts

const action = {

setCurrentPage ({commit, state}, page: number) {

// 更新当前store

commit(‘setCurrentPage’,page)

// 对应postMessage

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值