4代页面配置系统的探索实践

2,891 阅读15分钟

引子

时间过的真快,我在PPmoney已经工作了5年,这5年是忙碌的5年,也是成长的5年,随着PPmoney业务量的激增,前端团队不断面临新的挑战。有限的前端资源需要同时面对营销推广类需求,全流程类webApp需求,管理后台前端界面需求,因此前端团队必须开发更高效的系统去处理这些需求。

我们归纳了前端团队的营销推广类需求,有以下6种:

  • 专题页
  • 注册页,下载页
  • 运营报告
  • 营销活动
  • 大型活动
  • 埋点统计

这些需求中大部分是可以使用配置系统自动生成的,按照配置的难易程度,又可以分成以下三类:

  • 静态页:偏静态,交互简单,不涉及业务数据
  • 固定逻辑页:业务逻辑强耦合(与运营系统耦合),复用程度高
  • 定制页面:业务逻辑复杂,复用程度低

为了从这类繁复的工作中解放出来,PP前端团队一路摸索,自研了4代页面配置系统

  • 石器时代:静态页面+动态内容
  • 启蒙时代:可视化拖拽布局,组件化初步探索
  • 原子时代:较成熟的组件,逻辑与组件强绑定
  • 工业时代:UI和逻辑高度可配置,细化组件粒度,与业务系统0耦合,并兼容龙猫,朱雀玩法

本文将呈现各时期PP前端团队遇到的挑战,以及为反击而研发的配置系统,希望对您有些许启迪。

key-value配置系统——葫芦娃

葫芦娃是一个key-value配置系统,将页面可变的文案,资源地址等抽离成一个json配置,提供接口,页面读取配置进行内容填充。这是一个简单的配置系统,但当年,真的解放了我们很大的生产力。

进一步的,我们在葫芦娃上配置样式信息来改变页面主题;配置开关来实现逻辑控制;甚至配置环境变量来切换环境。

key-value配置是非常灵活的配置方案,以至于现在某些场景我们还会用它来处理。但是,它并未改变我们重复循环的工作状态,因为它:

  • 没有彻底解决简单切图拼装页面的需求
  • 没有生成副本页面的能力

因此,我们萌生了搭建一个静态页配置系统的想法,它可以为我们减轻当时约60%的工作压力。

静态页配置系统——龙猫

开发龙猫系统我们居然只开了2次会议就讨论出了基本方案,回头想想,那时真是草率,鲁莽,一帮不懂世事的愣头青就撸起袖子开干了。

龙猫系统是通过拖拽拼装页面的系统,提供了图片,文字,按钮,弹窗等组件,上手难度极低。

搭建龙猫时,前端技术还处于jQuery时代,我们的主要精力花在了如何实现拖拽操作改变DOM树结构,表单配置组件属性、样式如何实时反馈到预览图上。

我们梳理了以下思路

拖拽等操作作用于组件,通过angular.js的指令调用stage.js的方法管理树结构(stage.js是自研的管理树结构的库),调用stage.js的方法输出改变后的DOM结构,以提供预览。保存页面时,直接从stage.js中输出json,存到数据库中。

可惜那时还不认识react,我们还得自己去处理单向数据流的问题。

龙猫系统发布后,我们也把所有专题页的工作移交了出去,由业务部门的同事自己拼装页面,基本上所有PC端、移动端的静态页需求都解决了。

龙猫系统服役期间,累计发布页面2000+,直到龙猫X系统上线,它才逐渐退出历史舞台。

营销活动配置系统——朱雀&皮卡丘

龙猫系统的成功,确确实实让前端小组的地位得到了提升。这时,一个营销活动配置系统的任务又落到了我们肩上。

由于日常运营需要定期上线活动以促进拉新、日活、复投,而一个活动的开发涉及多个系统,开发周期往往需要1周甚至更多,尤其在指定节假日,上线时间有严格要求,在紧迫的需求面前,开发资源一次次告急。

在这样的历史背景下,朱雀系统的建设被提上了日程。

朱雀系统需要:

  • 支持标准活动事件:首次投资,再次投资,累计投资额达到XXX元等
  • 支持触发规则配置:单次达到、累计达到、时间约束、新老手约束等
  • 支持活动模板配置:Banner、规则弹窗、礼包、转盘、抽奖等
  • 支持活动一键发布:后端规则生效、生成前端页面

前端的主要任务是解析朱雀系统配置的组件信息、活动信息以生成页面。

这个系统的特点是:

  • 组件化,组件堆积拼成页面
  • 组件粒度大,以礼包、转盘为组件
  • 组件与活动系统高度耦合,支持高度定制化需求
  • 按需打包组件,以控制生成的js体积

朱雀系统上线后,约50%的日常运营需求得以满足,但几个月后,朱雀系统的问题也很快暴露出来:

  • 不灵活:组件粒度大,组件内样式无法灵活配置,需开发参与
  • 耦合强:前端展示与数据强耦合,难以复用
  • 拓展性差:前端渲染引擎无法与其他系统对接,限制了使用场景

这造成之前的页面开发需求变成了组件开发需求,开发量虽然减少,但系统复杂度上升,两者结合起来,并未达到系统建设前的预期。

由于朱雀和活动系统的强耦合,在PPmoney集团化的进程中,无法为新成立的及贷提供支持,及贷前端团队研发了皮卡丘系统,以满足拉新注册页配置的需求。

客观评价朱雀&皮卡丘,据我了解,这是大部分公司的页面配置系统的类似的解决方案。单就实用性来说,它已经可以满足绝大多数的场景了,而且由于强耦合,也可以使配置难度保持在中等水平。如果想进一步提升页面配置的灵活性,难免会引起配置难度的提升。而配置难度的提升会阻碍配置系统的推广使用。这是我们研发了龙猫X之后的一些感想。

UI配置+逻辑配置系统——龙猫X

PP前端团队总有一种居安思危的意识。我们希望对配置最复杂,最灵活的系统发起挑战。

这个系统设计初衷是解决龙猫难以对接动态数据,技术栈落后和朱雀缺少可视化布局,应用场景单一,与业务系统高度耦合的问题。全新打造一套服务于产品、运营、推广以及开发者,集UI与业务逻辑于一体的前端可视化配置系统。

龙猫X系统的特点是:

  • 可视化布局,配置属性、样式、动画及事件
  • 组件化,一切围绕组件展开
  • 组件支持嵌套,分为实体组件和容器组件
  • 支持复合组件,由多个组件组合成新的组件
  • 提供动画配置
  • 提供基于订阅发布模型的事件系统
  • 0耦合,不与任何服务端耦合,方便接入任意系统
  • 面向生产的辅助工具
  • 按需打包组件
  • 为开发者提供组件开发模式

下面逐一说明。

可视化布局

可视化是自龙猫继承的良好交互体验。编辑器主界面上包括组件库、舞台、配置面板,即配置即可见

组件化

我们认为,绝大部分UI元素,逻辑,都可以组件化,组件间可以通过事件相互作用,共同完成交互逻辑。特殊的,有些组件只负责处理逻辑,并不需要展示,在龙猫X里它也是一个组件,这点和朱雀有很大的不同。

在朱雀系统中页面逻辑是由组件中的代码实现的,而在龙猫X中,组件的粒度被细化,一种特殊的逻辑也可以抽象成一个组件。

举一个例子:定时器组件。

它虽然没有外观,但它可以处理特殊的业务逻辑:N秒后通知其他组件执行事件。

再举一个例子:依赖组件。

它可以处理这样的场景:页面加载完成,我们要发起3个请求,3个请求都成功之后,我们再去更新页面上的某些元素。

我们当然不希望串联地发起这3个请求,我们希望同时发起这3个请求,这是就可以使用依赖组件,当N件事情完成后,触发其他事件执行。

组件嵌套

组件嵌套的存在,允许我们将布局和块元素予以区分。使得块元素继承布局组件的某些样式。

例如:图片组件,我们希望它既可以出现在顶部,又可以出现在一个弹窗里,怎么做呢?为它加一个position: fixed的样式配置吗?如果这样做,那么文案呢?按钮呢?也都这样处理吗?显然,这样穷举是不合理的。

我们仿照html的组织形式,允许组件进行嵌套,我们只需开发一个position: fixed的浮窗容器组件,把图片放进去即可。这种处理方式是一劳永逸的。

除此之外,左右分栏布局,整屏滚动效果,tab页,甚至滤镜,都是我们认为的容器组件。

复合组件

我不知道你是否使用过PPT中的组合功能。如果你使用过,那么你就可以跳过这一部分了。对,就是那个东西。

我们允许一些组件组成一个大组件,新生成的大组件,使用起来和基础组件完全一致。

贴个示意图帮助你理解。

可以通过这个功能,将细粒度的组件组成一个粗粒度的组件。

动画配置

龙猫X内置了animate.css所支持的常用动画,同时支持导入css的keyframe关键帧动画。支持延时触发和事件触发。

事件配置

事件机制

龙猫X使用订阅发布机制处理组件间通讯

组件主动触发的事件,我们称为主动事件;依赖于其他事件而触发的事件,我们称为被动事件

一个完整的事件归纳为以下模型:

参数传递

业务场景往往需要在组件间传递参数,某些主动事件会传递一些数据到下一个组件,某些被动事件也支持一些动态输入的参数,我们在主动事件触发之后与被动事件触发之前,分别提供了处理参数,对参数进行格式转换的机会。在处理函数编辑面板,可以通过变量名为payload的变量获取到上一节点的数据。这一个模型归纳为:

业务场景往往需要跨组件传递参数的情况,例如A——>B——>C,例如我们想在C组件获取到A组件得到的数据。我们支持全局变量的设置与获取,可在A组件使用global.set("key", value)的方式将数据保存,在C组件使用global.get("key")的方式获取之前的数据。这一个模型归纳为:

事件端口

业务场景往往需要一个组件的主动事件触发多个组件的被动事件,例如,点击按钮后,同时进行发送请求、展示loading、数据埋点3个操作。这种情况下,我们引入了端口概念对事件进行分流。每个分流需要满足一定条件才会触发,一个分流可以连接多个被动事件,这一个模型归纳为:

事件来源鉴别

业务场景往往需要被动事件所属的组件判断主动事件所属的组件的身份,例如:按钮A和按钮B的点击事件,都连接了信息提示组件的弹出消息提示事件,如果弹出的提示有所差别,则信息提示组件需要判断此次触发弹出消息提示事件的是按钮A或是按钮B。这种情况下,我们通过将主动事件所属组件的身份信息记录在payload中,则在被动事件的数据处理过程中即可判断事件来源。这一模型归纳为:

0耦合

龙猫X是在PPmoney集团化发展的进程中诞生的产物,因此天生重视解耦。

龙猫X不与任何系统相耦合,与服务端交互可以通过接口组件,接口组件允许配置接口地址,请求方式,请求参数等等。理论上所有前后端分离架构的后端系统都可以接入。

面向生产的辅助工具

由于在龙猫X平台制作复杂页面需要大量配置,因此我们提供了很多辅助工具以配合使用。

代码查找工具

事件关系

可以快速了解组件间的交互逻辑,方便维护和交接。这个工具往往是新接手一个页面最需要的工具。

页面数据导入导出

页面数据是一个标准json,支持将页面数据导入导出,用于将dev环境页面导出到线上环境。

草稿功能&上线自动打tag&一键回滚

龙猫X是生产级别的工具,有系统日志,编辑草稿,上线版本管理,一键回滚等功能提供质量保障。

页面diff工具

类似于git的diff工具,可以对比两个页面配置的异同,可以参照《可视化配置系统页面批量更新工具的开发实践》一文了解更多详情。

自动化测试

利用事件链条自动生成了测试用例,自动执行用例,以保证页面逻辑是复合期望的。

这一功能我们仍在开发当中。

按需打包组件

发布页面时,龙猫X会分析页面所需的组件,为每个页面单独打包生成一个com.js文件,用以提供渲染页面所需的组件。

开发者模式

龙猫X为开发者提供了熟悉的命令行工具,用以初始化组件开发项目。

调试组件时会提供类似微信开发工具的仿真环境,可直接在测试环境开发调试本地组件,可与已发布组件相互作用。

思考

关于组件粒度。

尽管龙猫X提供了细颗粒的组件,但并不代表在龙猫X上只能最基础的组件去搭建页面,如果一些组件的逻辑是稳定的,或是非常非常复杂的,我们推荐将其开发成一个粗粒度的组件来使用。

总之,龙猫X是一个兼容龙猫和朱雀玩法的配置系统,我们希望把如何使用的决定权交给使用者。

什么是一个有价值的系统。

我大致把系统建设分为三个层次,每个层次都会有价值的提升。

  • 第一层次——信息整合:将零散的、散落在各处的数据集中管理。
  • 第二层次——格式化:将系统录入数据和输出数据的形式进行优化,这将带来使用体验的极大提升。例如将表单录入的方式改为拖拽录入,或将输出数据由表格改为图表等等。这一层次是将输入和输出的信息进行格式化,只是形式发生了改变,但是需要录入的信息量并未减少。
  • 第三层次——智能推荐:通过分析录入信息者的意图进行智能推荐是可以大幅降低信息的录入量的,这将更进一步降低系统使用的难度,从而提升整体效率。

正如上文所说,当前,我们还停留在工业时代,有了应对大规模定制化页面配置的能力。但是,我们做的事情大多停留在第二个层次,将页面生成的方式由纯撸代码改变为拖拽+表单配置+低代码的方式,这是一种信息录入的格式化,它降低了信息录入的难度,使得一些非技术人员可以参与到页面生产的过程当中。但是信息录入量依旧没有减少,随着页面复杂度的提升,页面配置和维护的难度依旧不低,这是我们需要关注和提升的。

现在市面上已经有了一些智能化的方案,例如图像识别生成页面布局,我想,这应该是配置系统未来发展的一个重要方向。

written by:张帆@ppmoney