掌门 “积木” 平台 -- 运营页面组件化解决方案的探索与实践

一、项目背景

运营系统研发部负责公司大部分 BU 的活动页面,渠道投放页面和营销 M 站点等页面的开发和维护。由于大部分活动页面的生命周期较短,长年累积下来后,在代码仓库中冗余了大量的弃用代码。对这些代码进行清理要面对不小的风险,和随之增加的工作量。另外,随着瀑布流工作模式的淘汰,研发更加无法抵挡“合理”的需求变更。很多改动很小的需求也需要执行完整的发布流程,对研发而言占用时间、对需求方而言响应不够迅速。

经统计,我们现在线上已经有近千张各类活动页和落地页。这些页面的基本管理能力(上下架,流量监控)和页面中相似功能的抽离复用就成了我们所处业务场景中的痛点。解决这些痛点能够帮助我们摆脱一部分枯燥乏味的 Coding ,提高人效,也能有效提高产品稳定性。所以我们迫切的需要一个操作相对简单的、可视化的页面配置工具。

值得庆幸的是随着前端技术的飞速发展,工程化和组件化已经非常成熟。现在可以轻而易举的借助 NodeJS+webpack+VUE/React 来开始我们的探索与实践。

二、设计思路

宏观上讲,我们希望可以通过各式各样的组件来拼装成一张页面。把冗余在仓库里的代码变成冗余在数据库的数据,最终可以形成一套支持渐进式扩展、容量无限制的运营工具。介于以上诉求,我们把平台分为三个模块,分别是:组件工程、运营侧和终端侧。由组件工程来生产组件、运营侧使用组件来配置页面、以及用户终端侧来输出配置好的页面。

整体设计思路如下图:

[图片上传中…(image-754db2-1595657704813-18)]

在这一部分,先简单的介绍一下各个模块的具体职责和设计思路。我们不是计划用组件来拼装页面吗,所以首先要确定组件在页面上的加载方式,继而确定组件的开发方式。那么就有(1)在客户端 runtime 时拼装,(2)在运营侧保存配置时拼装这两种方案。其中第二种拼装方式需要在保存配置时把所有选中的组件打包成一个 js 文件,这样对首屏速度有比较大的影响,并且在依赖的组件发生迭代后难以更新等一系列问题。所以目前我们选择了第一种方案,即在终端页面运行时加载拼装页面。

三、具体实现

1、组件开发工程

长远规划上,我们准备把成熟之后的积木平台开放给整个技术部使用,为部门赋能。所以组件的开发工程就要支持多团队协作,比如使用 monorepo 来管理工程,好处是开发组件时的公共依赖更容易迭代,不会给开发者带来太大的麻烦。另一种方案则是『卫星工程』,我们提供一个脚手架来帮助组件开发者生成样板代码,这样做能够减小工程的管理成本,并更大程度上增加组件开发者的自由度。

还是回到正题。

最终在终端页面上肯定是要有一个组件调度者来负责加载、编排和渲染组件的,所以我们编写了一个轻量的“渲染器”来完成上述功能。为了尽量减小“渲染器”的体积(因为这将是决定积木页面首屏速度的关键),我们没有使用 AMD 、 CMD 的加载方式,因为那势必会导致 requirejs 等工具的引入。

综上所述,我们需要的是一种以 UMD 方式加载,互相之间低耦合,自身逻辑高内聚的组件文件。如下图:

[图片上传中…(image-baf1e2-1595657704813-17)]

以实际被使用次数最多的组件《图片组件》为例,开发时除了组件文件夹名称要保持全平台唯一、入口文件必须是 index.js 之外。我们编写组件时并没有额外的学习成本。文件的摆放也没有任何要求。

最重要的就是 index.js 文件的内容,如下图:

[图片上传中…(image-9acef9-1595657704813-16)]

  • 引入组件的实现文件(目前积木平台只支持 vue 技术栈)

  • 配置“拾取器”。拾取器就是组件 props 的数据来源

  • 导出组件对象

当然,拾取器的配置也不需要自己编写,在组件沙箱中可以自动生成。这个后面详细说明。

最后一步是构建,把我们的 vue 代码构建成 js 文件。如下图:

image

在组件开发工程根目录中执行 yarnlibimage 即可。其中 image 是组件文件夹名称。

最终会得到一个可以在积木运营平台上传的组件文件,如下图:

[图片上传中…(image-785bfb-1595657704812-14)]

image

2、运营侧平台

运营侧最重要的任务有两个,维护组件生态和配置页面。所以重点介绍一下这两个模块的设计和使用。

首先是组件管理模块,积木平台的核心在此。我们需要一个积累组件的过程,这个过程也许会比较漫长,但是随着组件生态的日益成熟、完善,我们在积木平台上搭建页面的速度就会越快,成本就会越低。也正因如此,组件管理模块就需要一个比较完备的机制来支持不断增加的组件,不断趋于复杂的场景。正好我们就借用上面产出的 image.js 组件演示一下目前组件管理的具体实现方式。大概步骤如下:

1.在组件列表中点击【添加新组件】

image

2.上传组件并完善组件信息

image

目前组件的类型分为:基础组件、按钮组件、表单组件和活动组件几大类。组件分类目前主要是方便查找,技术处理上没有本质的区别。之后会支持的 html 片段组件和逻辑组件就会有比较大区别了。

3.发布组件

一旦组件保存成功了,这个组件就处于测试状态。组件管理者可以在列表中点击【发布】将此组件变为已发布状态。这时就可以在搭建页面时查找到这个组件了

4.组件的维护和迭代

上面提到组件管理是有一套相对完备的机制的,除了组件本身的测试中,已发布,迭代测试,已停用等状态外。组件的迭代还有一套类似发布系统一样的流程设计。如下图:

image

组件的每一次迭代都经过“迭代测试”,迭代版本发布之后,组件的工作版本就会切换到本次迭代的版本。每次的迭代版本都会一直保留(处于非工作状态),如果当前工作版本的组件有缺陷,可以通过“启用”其他版本来实现版本回滚,确保线上页面的稳定性。

接下来是页面管理模块。除了已经创建页面的基本管理功能(页面有效期,页面上下线等)之外,最主要的功能就是提供了可视化的把组件拼装成页面的工具。所谓的“把冗余在仓库里的代码,变成冗余在数据库中的数据”也是在这一步完成的。不难理解,每一张页面都是创建者有目的堆砌的数据。数据保存了页面中有哪些组件,顺序是什么样的、有哪些自定义布局、每个组件都设置了什么样参数,仅此而已。所以说理论上积木平台的页面容量是无限的。

配置页面的工具如下所示:

image

最左侧的是组件库,可以查到所有已经发布的组件。组件信息中包括该组件被使用的次数,当然使用次数多的组件在同类型的组件中会是比较稳定的。

中间区域我们称之为“画板”,组件被添加,或组件的参数被调整之后可以实时展示在画板中。画板中展示的效果几乎就是页面在线上展示的效果。为什么说是“几乎”呢,因为现在的画板就是一个 div ,并不能很好的展现有浮动能力的组件。这个我们会用 iframe+bridge.js 的方式重构掉。

最右侧有三个 tab ,第一个是当前页面的基础描述信息,如页面在平台中展示的名称、页面在终端展示的 title 、页面背景图/颜色、页面分享信息等等内容。第二个和第三个 tab 如下图:

image

当组件被选中后,第二个 tab 用来展示组件的属性拾取器。当拾取器获取到了值之后,就会实时同步给组件,组件就会实时的展现出最终效果。这当然得益于基于 MVVM 模式打造的 VUE 框架,可以很轻松且优雅的实现。第三个 tab 是用来调整被选中组件的布局属性的,可以实现一些简单的兄弟组件层叠效果。对比图如下:

image

image

为了确保积木平台能适用于更多的场景,我们还设计了很多细节,比如多域名的支持、渠道设置及路径自定义等等。篇幅有限,这里就不赘述了。

3、终端侧应用

终端侧的应用目前只有移动端,是积木平台的成果输出模块。在运营配置的页面都由终端侧应用来负责呈现。没错,你想的对,所有的积木页面都由这个单一应用输出,一旦这个应用跪了,全网的积木页面都会无法访问。这是我们面临一个挑战,当然为了不让这样的事情发生,我们也想了一些办法,比如接入阿里 node 性能平台、从 nginx 层面作 4xx , 5xx 的重定向等。日后我们的流量起来了还会考虑批量生成静态页面,双部署等手段。

终端侧应用是有服务区的,我们使用了阿里的 eggjs 来搭建,主要是考虑积木页面的打开速度,不能让客户因为我们的性能问题放弃我们不是嘛~!有服务区的前端应用无疑维护成本是高的,但是好处也不少,我们能从内网高效读取数据库中的数据(通过 java ),比如页面的基础配置信息, abtest 信息,甚至积木页面也可以接入 apollo ,即能实现重要功能的无发布开关,也可以支持 SSR ,从而支持 SEO 。

终端应用呈现页面的过程如下:

image

还是因为有服务区,所以我们充分利用了 http2.0 推送资源的特性。比如页面上有 10 个组件,这些组件的 js 文件并非全是由渲染器去加载。我们会通过 SSR 在页面上放上 3 到 4 个 script 标签,让这些 js 文件随着页面 dom 流一起被推到客户端。这样可以让首屏更快的展现。如下图所示:

image

积木平台整体的设计和实现基本上就是这些了。十分感谢,能看到里可能您对这个积木平台还是有点兴趣的。那么容我再安利一下积木平台的配套设施。

四、配套设施

**组件沙箱:**组件沙箱是设计给组件开发者使用的,开发者可以在沙箱中调试尚未发布的组件、可以为组件配置拾取器以及生成 index.js 中的配置代码。

1、为组件配置拾取器并生成代码

假设我们开发了一个图片展示组件,这个组件需要一个 prop , name 是 src 。组件把 src 赋值给 img.src 用来展示图片,这是很常规的方式是吧。那么在搭建页面的时候就需要指定这个组件 src 的具体值,暴力一点的我们提供一个文本框让页面定制者自己去填(这其实也是一个文本拾取器)。更好的办法肯定是我们提供一个图片上传工具,然后把上传之后的 ossurl 传给 src ,这就是所谓的图片拾取器。如何能让这个组件在画板中被选中之后展示出正确的拾取器呢,这就是为什么组件的 index.js 文件中需要一个 pickers 配置。

下图展示了如何为一个 Prop 关联一个拾取器

image

通过【添加组件】选择一个测试中的组件,然后点选右侧的拾取器,就能为当前选中的组件关联一个拾取器了。这时点击”查看 Code ”就能生成 index.js 中的代码了。

image

2、在沙箱中调试组件

其实组件中还有一些更“高级”的功能,为了实现兄弟组件中的通讯,我们在渲染器中加入了发布订阅模式。组件可以通过 vue.prototype 中的 api 向消息中心广播消息,其它组件可以订阅指定的消息,这样可以实现兄弟组件间的通讯。

那么问题就来了,这些 vue.prototype 中的 api 在我们开发的时候是不存在的。因为这些 api 是在 runtime 的时候由渲染器“注入“到组件中的。但是有了沙箱之后,我们就可以在组件未上传的时候调试类似的功能了。不仅如此,沙箱还可以把正在开发的组件实时的渲染在沙箱的画板上,让开发者立刻看到组件在平台上的表现。步骤如下:

1> 还是以 image 组件为例,在组件工程中执行 yarnslibimage ,会启动一个 socket 服务

[图片上传中…(image-bbb9b5-1595657704812-1)]

2> 在积木平台 → 组件沙箱中点击【devServer】按钮,平台会去消费刚才启动的 socket 服务,这个服务会自动构建组件代码放在内存中,并将构建好的组件渲染在沙箱画板上。

[图片上传中…(image-92423-1595657704812-0)]

3> 在我们的 IDE 中修改组件代码后,在平台上点击【刷新】按钮就能立即看到组件被修改后的样子了。我们也马上会支持在代码 save 的时候自动更新~!

至此,组件的开发就是一个相对完整的闭环了。对于低代码搭建移动端页面的整体解决方案,我们还会不停的探索和实践,希望这个平台能赋能于技术部。真正有效的提升效率。3Q~!

五、作者介绍

李海亮,掌门1对1运营系统研发部前端架构师,负责运营工具系统架构设计、创新、实践和团队管理等工作。

服务推荐

猜你喜欢

转载自blog.csdn.net/weixin_48726650/article/details/107578144