背景与现状
(一)背景
“不要写死”——程序开发过程中经常提到的一个问题。我们运用各种设计模式进行抽象、封装,就是为了让我们的代码看起来不是那么“死”,从而能够灵活的适应各种“变化”。
如今互联网的变化,可以说世界的变化超出每一个人的预测,那么对于快速变化的市场与用户需求,尤其我们客户多样,存在或多或少的个性化定制需求,我们需要更加快速、高效地做出响应——页面可配置的概念应运而生,其包括UI(界面与交互)、业务、数据、配置等诸多方面。
页面的自定义比作盖楼,添加一个楼层,每层可以自定义内容,譬如商品、优惠券、商品排名等。在淘宝旗下的店铺装修平台——“淘宝旺铺“中,淘宝店家可以选择基础模块的内容,编辑首页或新建页面,动态配置页面。
淘宝旺铺装修App
淘宝旺铺装修PC
这种页面配置有以下特点:
- 静态数据或后台数据
- 单页(多图、图文混合偏多)
- 页面区块耦合度小,可灵活拼装
- 新建的页面可以快速发布上线
(二)现状
目前系统各业务模块业务耦合度较高,依赖较强,没有统一的配置管理方案与渲染实现,重复造轮子。
(三)设计思考
- 不能因为技术架构上对页面可配置支持的引入,带来新的开发成本和负担;
- 引入可配置支持的页面应该与引入前基本保持在同样的性能表现,不能因为页面可配置明显拖慢业务性能。
所以,在实现页面可配置的前提下还有两个目标:对业务无侵入、高性能。
核心分析
(一)核心问题
可配置页面,要解决三个核心问题:
(二)页面分析
头条号创作者首页
上图是我截取的头条号创作者首页的图片,我把它划分成了不同的区块,并且标上了编号。整个页面,我们将它称为画布(Canvas,或者叫做页面),里面一个个带有编号的红框,称之为容器(Container)。实际上,画布(页面)也可以理解为是一个足够大的容器,也是顶级容器,便于理解,暂时称它为根容器(Root Container)。
现在我们来具体分析一下这个页面:
- 整个详情页面被认为是一个完整的画布(白纸),由不同(大小不一)的容器构成,这些容器就像乐高积木,可以用它组装搭建成不同的页面;
- 容器由各种组件(Component)填充,组件包含数据源、显示样式、交互逻辑等一套完整的属性(Attribute);
- 组件中包含banner、按钮等单元,这类最小的组件单元,我们称为元素(Element),这一类元素逻辑上将无法再被拆分;
- 原则上容器可以由更小的容器构成,它们是递归构建的,呈树形结构,这是一个迭代实现的过程,本次设计暂时不考虑嵌套。
综上分析,我们可以得出五个概念:
- 画布(Canvas):整个页面,可以理解为一张白纸;
- 容器(Container):整个画布可以被拆分成不同的区域(Region),每一个区域称之为容器。在这次设计方案里面,暂时不考虑容器嵌套更小容器的问题;
- 组件(Component):组件是粗粒度的,包含部分页面的UI、数据以及与之相关的业务逻辑,容器由可以由若干个组件构成,组件填充容器的显示内容(其实组件也可以称为模块,之所以称为组件是为了后续丰富通用组件库考虑);
- 属性(Attribute):单个组件包含数据源、样式、交互逻辑等一套完整的属性;
- 元素(Element):组件包含若干页面元素(banner、button、tab、label等),它们是逻辑上最细粒度的单元,不可再被拆分,本期设计不细化到元素层面。
(三)页面布局
有了以上概念定义,我们再回过头看这个页面。从布局层面来看,它就是一个网格布局,以从上到下,从左到右的顺序逐个分布容器与组件,但是就这个页面来说,它还不是简单的横向纵向网格。
网格化布局
我们将前面头条号创作者首页的内容去掉,只留下布局,那么就是上图中这个样子,为了简化逻辑,我们把容器都设计成流式布局,所有组件一个个自左往右,自上而下堆叠排列。
(四)页面内容——组件
每一个组件都有自己独立的逻辑和UI,组件之间完全解耦,这样就可以很方便地通过排列模块来完成不同的页面定制化需求,使一个页面可以展示不同的内容。原则上,组件并不依赖某一具体页面,组件也可以在不同的页面之间进行复用。
- 组件采用声明式编写,本质是一个json对象,封装由具体的框架去实现
- 由于声明式的组件是一个json对象,当扩展组件功能或者改变组件行为时,可以通过覆写json对象的方式实现,最后交由框架封装
(五)页面交互
- 传统页面元素事件:点击、移入、移出、弹框、跳转页面等
- 业务事件:提交前、提交后、数据后处理等
问题与思考
(一)布局复杂
页面布局不一定是网格化有序排列,布局样式多元,那么容器层面怎么支持与兼容是需要面临的问题
(二)组件复杂
业务组件复杂,组件入参形式多样,接入方式如何统一
(三)交互复杂
弹窗、跳转、校验、提示,各种前端交互纷杂多样
(四)业务需求迭代
- 随着业务需求不断迭代,组件如何适应变化
- 组件的状态数据和函数方法可以通过上述方法覆写,但是视图模板的变动如何支持?
- 组件必须遵照容器布局系统规则暴露一些特定的属性和方法,便于注入一些数据源对象,便于容器布局系统统一管理组件
- 画布定义好以后,在拖动组件到画布中编辑时,是否能修改画布布局? 如果能修改,技术复杂度是否可控?
(五)其他思考
- 布局配置、渲染独立,不与业务系统耦合,制定组件接入规范;
- 每个组件需要独立打包,组件内容牵涉业务数据调用、公共组件的调用封装问题;
- 单个组件样式、业务相关数据与逻辑独立管理,组件之间互不影响。
规划与步骤
- 功能:把组件库做得更丰富,尽量提供更多的基础组件
- 易用性:扩展组件自定义功能,支持更多的属性配置
- 落地:适应新需求和变化