【环球聚看点】组件化元数据结构设计——PageMaker

发布时间:   来源:CSDN  

Page Maker是我在公司参与时间最长的一个项目了,自打实习期做了webIM,转正之后不久,就开始参与了PageBuilder项目的开发,前前后后大概经历了两年的时间,后来由于公司业务调整,已经在今年初逐步将项目交接了出去。虽然现在不再参与项目的维护,但是从这么长时间的开发过程中,还是学到了很多东西,也让我成长了很多。其实最关键的,并不是具体解决了什么问题,而是解决问题的思路与方法。

PM 的产品定位是一个平台,他所提供的核心能力就是页面的可视化构建,通过拖拖拽拽,就可以生成一个页面,并所见即所得。为公司的各产品先以及后续的ISV提供稳定 可靠 自由的页面定制化能力,以此满足客户的多样化需求。

在实际的开发过程中,我们遇到以及需要解决的问题有:


(相关资料图)

组件化元数据结构设计组件开发规范运行态资源加载与性能优化基础依赖管理

其实每个点都能单独写一篇文章的,这里就只简单总结了。

组件化

PM的实现中,页面组件化是一个非常重要的前提,所以我们抽象出了两个概念,即:

布局组件视图组件

布局组件用以实现页面布局,而视图组件承载着业务数据,并以多样化的形式展现给用户。所以页面长得好不好看,直接取决于视图组件。 另外,为了提高视图组件的可扩展性,以及凸显PM所强调的 可配置的能力,我们还抽象出了一个组件类型,即 属性组件

有了属性组件,实施人员就可以给视图组件配置不同的属性,比如数据源,展示形式,等等,对于视图组件来说,所有可变,可配置的,都以通过属性组件来实现。通过 PM平台,将属性组件和视图关联展示到页面上,并最终呈现给用户。

元数据结构设计

基于元数据的应用设计的最大的好处就是可描述和可扩展。我们将页面抽像为一套元数据结构,而pb就是这套元数据的解析器。保存页面,实质上就是保存了一份元数据的实例。

{/****组件列表: 每往页面中拖入一个组件,都会在该属性下新增一条字段,用于描述该组件****/"componentList": [{"editableData": string,//组件的可编辑属性    "cType": string,       //组件名称    "hasCustomProps": Boolean, //组件是否有自定义属性    "isContainer":Boolean,//是否是容器组件    "isSubcomponent":Boolean,//是否是子组件    "id": string,//组件id 唯一标识    "parentId": string,//父级ID    "componentHolderKey":string,//对应tab容器组件的tab    "appId":number,//组件属性应用的ID    "deletable":Boolean,//组件是否可以被删除    "url":string,//iframe组件URL地址,之前ocean组件用的}],/*****页面数据信息****/"pageSettings": {"title": "AAA",//页面名称     "template": "Classic",//页面模版       "layout": "grid"//布局类型 grid / vertical},//页面的可编辑属性pageProperty:{"editableData": {},}/**section列表,注意 有的section是没有位置信息的,如容器组件内的组件,是根据数组顺序依次由上而下排列**/"sectionList": [{"id": string,//唯一的标识       "targetLayout": string,//所属的工作区       "x":number,//x坐标       "y":number,//y坐标       "w":number,//w 宽度       "h":number//h 高度}, {"id": "8d227680-f26a-11e6-9457-8f2c27c19ddf","targetLayout": "default1"}]}

组件开发规范

在这个问题下,细分的问题还是挺多的比如:

1. 技术选型

这个其实没有太多可说的,公司整个平台都采取的React框架,所以为了保持统一,当然就是直接采用React

2. 组件开发模版

这里的模版设计所遵循的原则应该是尽量简洁,符合常规的开发习惯,并将细节封装好隐藏在内部。我们会提供一条龙的服务,包括但不限于构建方案 组件注册 以及对接CI服务,对于业务方来讲,只需要关注于组件的开发即可。另外,模版设计时,一定要遵循模块化 分层设计的思想,尽量减少后续模块升级所带来的迁移成本。

#源代码目录,项目基础结构,里面列的文件必须按照这个结构存在,其他可按照自己的需求安排。/src    #export组件的文件,必须存在    index.js    #Page Builder属性配置代码存放的目录    /props        #export 属性组件文件        index.js#包信息描述package.json#文档README.md#git忽略文件列表.gitignore#npm忽略文件列表.npmignore# webpack配置 可选 支持自定义扩展webpack.config.js

4. cli工具

前端工程化设计中,一个很重要的原则就是自动化,将所有重复性的,工程化的工作,交给程序完成,从而尽可能的提升工作效率。所以我们开发cli工具,将组件注册 git项目搭建,以及生成本地开发模版的任务全部集成在cli工具中,开发人员一分钟的时间,就可以开始新组件的开发

5.css 模块化

相对于JS的发展的来讲,CSS的进度确实缓慢了些。因为PM页面上的组件都是独立构建的,但是会放在同一个运行态下,所以很容易出现样式覆盖的问题。为了保持样式的稳定,作为平台,必须提供统一的css 模块化方案。经过技术方案的调研和对比,最终采取了styled-component。至于为什么不采用css module,是因为他的hash是在构建过程完成计算的,而不是运行时,所以不能从根本上解决PM平台所面临的问题。

6. 版本管理

对于组件的版本管理,我们没有采取hash,转而采用了npm包版本的方案。主要是考虑到更加直观和友好,而且方便版本回退。

7. 上线发布流程

对于开发 测试以及线上环境,静态资源都是共享的,所谓上线,只不过是将对于环境下的版本更新下而已。后端同学提供了导入导出工具,方便组件的发布上线。

8. CI持续化集成

每个组件都对应独立的前端项目,我们为这些项目接入CI服务,并将组件的构建,版本发布以及静态资源发布等等集成在CI任务中,另外,为了方便进行组件的性能优化,我们借助webpack 插件为每次构建都生成一份构建报告:

基础库管理

首先所面脸的问题:在我们的设计中,PM的每个组件的都是独立的,单独构建的。因为组件开发都需要依赖React,如果在构建时将React构建进去,那么运行态下就会存在多份react,而react是不允许多实例共存的,所以这就产生了问题。除了react,还可能依赖其他一些比较大的类库 比如recharts。

关于这个问题,我们最开始采用的方案是 webpack dll。一切看起来都是那么完美,直到我们在升级react 16 的过程中,痛苦出来了。因为react升级,所以我们要构建出新的dll来,dll更新了,那么原来那些发布过的组件,都需要基于新的dll重新构建一遍,才能在运行态下正常渲染。OMG,工作量太大了,而且涉及到多个业务线下的上百个组件。问题已经比较严重了。

经过商量,我们最终用webpack external 代替了 dll的方案,虽然dll在运行态下的性能会稍好一些,但是所带来的问题已经远远超过收益了。

运行态资源加载与性能优化

前面提到PageMak是一个平台,各个业务方都会在这个平台上开发和发布组件,然后在运行态下统一加载和渲染。那该怎么加载 加载哪些,才能达到更好的页面加载性能,就是一个很值得研究的话题。 关于这个问题的方案,其实演变了很多。

野蛮时代

刚开始的时候,按照我们的设计,每个业务线都有一个前端项目,这个项目下维护着该业务线下的所有组件,也就是说,所有的的组件都会打包在一起,单个组件没有独立的版本控制。所以,顺利成章的,这种情况下,我们在运行态就按照业务线来加载组件,如果当前的租户开通了应用,那么,就会去加载这个组件。这也是最初的设计,后来做了一点点的优化,就是判断下,如果当前的页面上拖入了某业务线下的组件,才会加载这个业务的组件。这里的判断逻辑由后端同学实现。,前端只负责加载和渲染。

工业社会

后来,随着业务线组件的增加,这个项目变得越来越庞大,如果某个页面上只拖入了业务线下的一个组件,那么该业务线下的所有组件都会加载进来。而且更严重的是上面提到的,单个组件没有的独立的版本控制,这其实是很要命的。所以为了解决这个问题,经过商量过后,我们决定将单个组件拆分为独立的前端项目。基于此,我们在运行态下资源加载的颗粒度,就可以控制到组件级别的了。

也就是说,我们通过元数据分析,可以得知该页面上拖入了哪些组件, 以及组件的版本。然后,加载模块就会将所有的组件通过异步脚本的方式加载进来,交给渲染器去渲染。(至于渲染器如何知道哪些组件加载进来了,哪些组件没有加载进来,这是通过一个组件的注册模块来实现的。)

方案走到这里,已经将所有的冗余的资源都去掉了,看似很完美,但其实还是有问题的。

首先,一个页面上可能会拖入很多组件,少则七八个,多则一二十个,每个组件都对应一个js资源,那么要想页面渲染出来,至少将这些js脚本全部加载完成。但是,客户端对于并行请求数是有限制的,没记错的话应该是5个,从这方面考虑的话,前端性能优化的一个很重要的方向就是资源合并,比如常见的雪碧图。

相关文章Related

返回栏目>>