前言
最近在掘金上写文章,偶然看到了系统推荐的前端早早聊的活动。
应该一直都有,但是之前没怎么注意。
这次已经是第三届了,主题是前端搞搭建。
我在前端的工作经验较少,而且所任职的公司都没有这类系统。
出于好奇心,决定看一下,大厂是如何做搭建系统的。
这是讲师团队的阵容。
据说讲师平均水平都是阿里 P8 级别的,单凭这一点,77 块钱的活动费真的不算贵。
大会的目的是解决如下困惑:
- 每天陷在业务中,机械的切页面写功能
- 想要提升开发效率,不知道从哪里下手
- 变来变去的营销页面,可不可以快速生成
- 前端技术一直变,切页面有哪些新的方式
- 一个可视化搭建工具,需要具备哪些模块
- 如果尝试开发搭建工具,有哪些实现难点
在观看大会之前,我和很多没接触搭建系统的人有类似的看法,AI 自动生成 HTML 好像还是个天方夜谭。但通过短短一天时间,彻底改变了我这种看法。
“业务做不快,加班加不完”
“联调时扯皮,成就感缺失”
“公司不重视,前端地位低”
“需求做不完,技术没成长”
“那么多页面,能不能搭建”
这些问题,每个前端在每个公司或多或少都存在。虽然大会提供了针对可能性、方法论和技术实现的讲解。但我个人认为,在公司具体落地搭建系统,还是要靠如下三点。
- 公司确实需要该项技术。
- 技术人员个人主观意识,愿意主动推进。
- 公司技术管理层足够重视。
搭建系统的目的是为了提效,只有公司需要提效、开发人员需要提效,搭建系统才有意义。
搭建系统能力讲解
搭建系统的能力在我的理解里分为两种。
一种是拖拉拽生成 HTML 文件,然后配置数据源。一键部署上线,支持版本回滚等基本操作。
一种是根据根据设计稿生成 HTML,或者根据 json schema 智能生成 HTML。同样支持一键部署上线,支持版本回滚等基本操作。
第一种其实在几年前就有了,记得在 15、16 年那阵,bootstrap 就有可视化拖拉拽生成系统。但那都是些半成品,而且不支持数据源的配置等操作。
第二种的话比较难,因为需要用到人工智能和机器学习技术。在中小型前端团队里,很难实现这类能力。像阿里这种大型团队,目前探索的脚步已经走的非常远了。它们的产品非常强大,可以在 imgCook 上体验一下,处理一些简单的设计稿绰绰有余,但我尝试过处理复杂页面,效果很糟。
搭建系统特别适合电商营销运营场景,大名鼎鼎的鲁班系统就是为此而生。
像天猫、京东、淘宝这些大型电商网站,他们的活动页面基本上都是靠搭建系统生成出来的,当然有一些可能需要再进行微调。
普遍意义上,搭建系统的用户并不是开发人员,而是设计师或者运营。
而前端开发人员在其中担任的角色就是能力的提供者。把创造页面的能力通过搭建系统提供给运营、产品和设计。这个模式像极了依赖注入模式。
在我们技术人眼里特别简单的易企秀,其实就是一个类搭建系统。他们把创造页面的能力提供给完全不懂技术的普通人,只不过限制较多。
搭建系统简易版技术实现
实现搭建系统是一个庞大的工程,中小团队很难把这件事做好。
做好这件事,我认为需要具备以下条件:
- 有对技术有追求,爱折腾的人。
- 有足够的时间,搭建系统更像是一个产品,而不是单纯的开发工具。
我个人爱折腾,但没有时间。在看完直播后利用空闲时间(最近忙的要死,根本没有空闲时间,基本上都是在占用睡眠时间)做了一个简单的搭建系统。
因为技术分享的信息量太过于密集,很多东西没来得急仔细研究。现在 PPT 和录播也还没公开,所以很多细节都是靠自己的理解、琢磨和网上查找的资料来完成的。
借鉴了:
- 阿里淘系技术 - 鲁班系统
- 京东惊喜 - MPM 系统
根据计算机之子、知乎三大软狗之一的 winter 程劭非的专栏《重学前端》中介绍,实现搭建系统在技术层面大概是有 3 种流派。
- 模板化搭建,最老的那种实现。前端工程师生产模板,运营提供数据,组合起来形成页面。淘宝就是这么实现的,我们公司的老版 PC 商城也有类似的功能。
- 模块化搭建,由前端工程师提供模块,由运营组合模块和提供数据形成页面,属于模板化搭建的升级。
- 数据驱动界面,最高级的思路。
接下来的实现,我采用的是最高级的思路,数据驱动界面。
网络代理
我使用的是 nginx 作为网络代理。
直出端
需要有一个服务端来渲染页面,我本来选择的是 node 的 SSR 框架 next.js。next.js 的优点是可以同构,这也是 node.js 和前端契合的一个关键。如果选择其它语言,如 Java、Go,同构的地方就需要写两份代码。但是页面生成的不是 react 代码,所以我干脆用原生 node.js 做了一个类似于 next.js 的 SSR 框架。
initialPageData JSON Schema
使用 json schema 定义一份 initialPageData,作为生成页面的依据。
{
"pageId": "demo0007",
"appType": "1",
"dataSource": [
{
"url": "https://d.luzhenqian.me/api/demo0001",
"methods": "GET",
"resultMapName": "data"
}
],
"modules": [
{
"id": "m33",
"styles": {
"background": "#ffffff",
"font-size": "1.25rem"
},
"components": [
{
"id": "c103",
"elements": [
{
"text": {
"id": "e.txt00001",
"content": "演示",
"styles": {
"background-color": "#ffdd66",
"font-size": "1em"
}
},
"input": {
"id": "e.ipt.000001",
"defaultValue": "hi",
"styles": {
"background": "red",
"font-size": "1em"
}
},
"buttons": {
"id": "e.t000001",
"onClick": {
"url": "https://d.luzhenqian.me/api/demo0004",
"methods": "GET",
"params": "e.ipt.000001",
"callback": {
"jump": {
"pageId": "demo0007",
"initialParams": true
}
}
}
}
}
]
}
]
}
],
"dataCache": {}
}
这个 json 的格式很大程度上借鉴了京东惊喜的 MPM,但又有所改进。因为我支持了接口的调用等功能。
一些小的改进
我把一个页面上的所有可以看到的事物拆解成三类。
大的模块 module,中等的组件 component,最小的元件 element。
通过这三类事物的组合,构成一个页面。
module 一般是标题头,或者豆腐块列表等常用的区域,可以通过配置背景色、背景图、边距、宽高等来自定义样式。
component 和传统开发中的组件概念类似,但比传统组件稍大一些,因为它可能是复合型业务组件。
element 是最基本的元件,与传统概念中的组件一样,比如 button、text 等。
这个 json 存放在数据库中。由于这份 json 的格式是非常不固定且随时会变动的。MySQL 这类关系型数据库直接 pass 掉。mongodb 和 json 简直是天选之和,没有理由不使用 mongodb。
并且将页面的数据源升至顶层,在 SSR 阶段,通过 next.js 的 getInitialProps 方法在页面渲染前就把整个页面中所有的数据提取到,并且将数据格式简单的处理,映射成你想要的名字。你可以自定义 params 和 data 作为数据源的参数。你也可以配置多个数据源,服务端会自动帮你合并。这里面有一个很细节的点,多个数据源的返回数据命名一致时,并不会发生 js 默认的覆盖行为,而是使用合并策略。
个人感觉最难处理的点是交互,目前我只能给元件添加事件,并且事件只能是发送请求或者跳转页面,你可以选择这条请求是否需要附带参数,和指定哪些参数。这个请求可以触发回调,回调同样触发一个事件,该事件同样只支持发送请求或跳转页面。
版本管理、回滚和发布
至于版本的管理,技术实现上比较简单。功能主要是版本回滚与动态发布。
当保存 json schema 时,都会在 mongodb 中生成一个新的记录,而不会修改原来的记录,并给这个新记录一个版本号和一个标记。
如果需要回滚,只需要将标记移到对应的记录上即可。
这个思想是借鉴了 redux 的 immutable 思想,因为前些日子在写 redux 的文章,对 redux 的理解又加深了一些。因为 redux 的时间旅行就是依此实现的。
一键发布也是通过移动标记,每次移动标记时,都会在 直出端项目下修改一个 json 文件。
直出端项目通过 pm2 来部署,在部署时添加 --watch 参数,这样当 json 文件发生变化时,pm2 会 restart 服务端项目,重新在 mongodb 中获取当前的最新的数据。
在 restart 过程中,如果有页面请求怎么办?使用集群部署就好了。然后 json 变化时,创建一个任务队列,将集群中每个进程按顺序依次 restart。前一个服务 restart 结束,下一个再 restart。这样会保证系统的可访问性。
整体架构图
我付出的时间比较短,做出的东西相对简单,架构如下:
可以看到,整个过程都没有使用 docker,原因是 docker 实在是太臃肿了,我已经放弃使用 docker(除非服务器硬盘足够大)。
到此为止,虽然我实现了一个搭建系统最核心的部分,但距离生产还有很大的距离。
可优化的点
由于时间问题(实现上面这些事我用了两个晚上),很多细节都没来得及优化。
比如:
用户鉴权、系统配置、客户端页面
组件、模块的组合
json schema 的校验
可视化拖拽调整位置、宽高等
模块、组件的数据源配置(这个实现比较难,因为 next.js 不支持组件级别的 getInitialProps,但我有些思路)
动态路由的实现
编辑动态保存
目前生成的是原生 HTML,计划支持生成 React 和 Vue,并可以定制化代码。
搭建物料市场
错误自动上报
自动化性能分析
其它我还没想到的点...
搭建系统无能为力的地方以及痛点
无能为力的地方就是强交互页面,因为这不是搭建系统该负责的。
痛点有很多,但我觉得最难的是接口,因为那些乱七八糟的数据格式实在是难以梳理。这里就需要借助 BFF 层,把那些乱七八糟的接口整理成让人调用起来可以感到愉悦的接口。
写在最后
通过这次直播学习、自我总结和实践,带给我最大的收获是技术视野的增长。
当普通人还在接口扯皮调数据、怼需求切页面时,业界大厂已经开始研究如何自动化实现这些东西了。可以预见,未来几年,必然会淘汰更多的初中级前端,只有早早掌握最为核心的能力,晋升为高级前端才能在技术革新浪潮中幸免于难。
通过借鉴别人思路,自己思考和动手,实现搭建系统的核心部分代码。这也让我拥有了未来应对开发搭建系统的能力。我认为,编程这件事并不只是单纯去学,更多的是靠自己思考、自己动手。最后把所思所想所获所得整理出来,分享出去。
分享到此结束,希望对你有所帮助。