前言
随着前端开发的不断发展和改进,出现了越来越多的框架,工程体系,从Jquery时代到目前的React、Vue体系,前端开发从原先的单一开发页面,变成组件式、函数式编程,除了有更好的、更高效的开发体验外,复用、抽象、维护性也变的尤为重要,与此同时,前几年借助互联网的发展,移动端开发也成热门趋势,业务场景也越来越多,很多时间开发面临的压力也越来越大,我们开始思索如何通过有限的人力来提升工作产出。于是近年来,有一个词逐渐走入前端开发的视野,"低代码",又名LowCode,指的是通过图形化、产品化的工具和一部分少量的代码来完成原来需要全部代码完成的一种新的开发方式。
低代码的开发历程
低代码最初的发展可以追溯到2000年,从2000~2015年可以说是低代码发展的第一阶段,但是没有太多起色,也没有太多有亮点的产品,从2015年~2018年低代码平台开始逐渐升温,Mendix 和 OutSystems 相继获得资本的投资,低代码(lowcode)开始逐渐为人所知
低代码是什么
简单的说就是采用可视化产品方案+少量的代码完成原本全部需要代码开发完成的工作。
- 可视化产品方案
将原本可以用代码沉淀的方案,例如组件、模板、模块,通过可视化的形式沉淀在产品上,并通过拖拽、配置等方式将这些模块进行组装
- 少量的代码
通过少量的代码来增强平台的适应性,例如事件、回调函数等,这些逻辑很难通过产品化、可视化的方式进行沉淀,所以我们通过在线编码的方式进行开发
在移动端开发中尝试采用低代码方案
我从两年前加入阿里,进入1688事业部,简单介绍下我所处的业务环境。1688是中国领先的小企业国内贸易电子商务平台,以批发、采购为核心,提供从原料采购--生产加工--现货批发等一系列的供应服务。电商中有一个重要的概念叫人-货-场,其中场就是场景,场景由多个页面组成,简单的说就是这一类导购型场景,这么说如果还是不是很明白的话,我们看几张图就明白

虽然1688数据B类平台,但是批发商,甚至工厂也需要有场景将商品展示给商家,进入1688后,我一直从事导购类场景的开发,其中移动端开发占据大头。
前端移动端开发现状
了解了一些业务背景后我们来看下前端的移动端开发,从目前来看,前端的移动端开发主要分成以下几大部分
- 纯前端开发:采用React、Vue等框架进行移动端开发,通过浏览器、webview渲染
- 混合模式开发:代表主要是RN、Weex,主要是为了解决移动端的渲染问题,通过借助Native的原生组件渲染和js的逻辑,相比纯浏览器渲染而言,Native渲染具备优势,同时开发DSL又采用Vue、React等前端熟悉框架
- 新的类Native模式开发:例如Flutter,采用新的Dart语言,跨端的方案,接近原生的渲染性能。
- 小程序开发:随着小程序的火热,小程序的开发也是如火如荼,小程序大都采用类vue的独立DSL,能调用对应端提供的原生能力,相比传统H5页面性能更好。
技术选型上,我们为了能在页面渲染有着更好的性能,1688的前端移动端技术栈从H5已经开向Weex转移,采用Rax为移动端的通用DSL,Rax是淘宝提供的一套跨端开源框架,主要应用在移动端,能通过一套ReactDSL同时渲染Weex和H5页面,同时又能利用React生态。
技术栈统一了,在移动端前台领域,我们又碰到了其他问题
- UI更新频繁
相比于中后台领域,前台场景的UI迭代可以说是飞快,由于是面向类C用户,每个场景拥有主题,时间,品类,用户心智,品牌等众多因素,造成设计风格的千变万化,虽然长得都挺像,但就是不一样,同时用户心理喜新厌旧,如果同一个场景长期一种样式,会造成审美疲劳,设计师会在一段时间后更换设计风格。开发前台UI对于前端来说可以说是一个体力活,简单的CSS Flex布局,加上几个固定的基础组件,剩下的就是纯粹的UI还原,在大量的需求下,开发前台组件需要花费很大一部分精力在UI开发和还原上,机械、枯燥的开发模式,很容易让开发者厌倦,
- 基础组件的碎片化
相比中后台有比较成熟的基础组件库,例如AntD,Fusion等,前台UI带有比较强的业务属性,移动基础组件库一直很少有积累,加之Rax本身版本在快速的迭代,导致小轮子满天飞,但是又没有足够的管控和收拢,加之团队成员线下沟通不畅,很多为了快速完成需求,找到类似的组件fork后修改,时间一长,很多相似的功能的组件越来越多,但是没有一个在通用基础上组件上进行迭代,基础组件治理就成为一个问题。团队成员越多,这个问题就越明显,严重影响了团队的协同性和开发效率。
- 开发链路分散,
除了开发组件本身以外,我们还有很多跟这个组件相关的开发链路,这些链路往往和当前场景有关,例如数据源的动态模拟和调试,组件的新建、发布等流程,组件的预览效果,weex、web开发需要留意的一些特殊约定,这些细小的坑都散落在开发链路中的某处,等着开发者去踩,只有开发过很多次的老司机才能一一避免,新手开发体验极差。
- 视图逻辑耦合
虽然我们一直推荐将视图和逻辑在书写的时候进行分开,但是每个人的开发习惯,书写习惯,需求时间等因素影响后,更多的时候只要是完成功能,就不管什么规范了,各种写法五花八门,俄罗斯套娃般的HOC,让后续去维护的同学非常苦恼。
问题有这么多,那我们如何去解决?
我在加入阿里之前,一直从事手游开发,见证了游戏开发引擎(cocos、u3d)等可视化UI、场景编辑器对游戏开发的重要性,

虽然前端技术栈比较广泛,做通用性的可视化平台、引擎比较吃力,那针对上述业务特点的场景,是否可以用一套可视化的平台开发方案来解决问题?
通过平台的能力将开发链路,基础组件进行线上收敛,通过机器转码的操作来实现可视化到代码的统一生成,通过产品化+可视化的方式,提升UI开发效率,最后不停的优化开发平台,让这个人工的开发效率越来越趋向智能化。
于是在接下来的时间里,我开始思考如何用一套可视化+低代码的方式来为移动端开发提效。
UI快速开发
我们首先要解决的一个问题就是UI的快速开发,先从前台导购场景最常用的一个组件 导航+列表的推品组件开始看起

如上图所示整个组件我们可以分为几个核心的元件
- 导航:Tabheader,用于分类列表的切换。
- 文字:Text,用户显示标题、文案、价格
- 图片:Image,用于显示商品主图,按钮背景图等图片
- 列表:List,用于上下滑动,下滑翻页,还有楼层电梯定位等功能
UI可视化开发
有了上述基础的元件后,我们有了组件拼接的积木,我们称之为物料,接下来要做的是如何把一块块积木按照不同的组合进行拼接起来。我们发现,组件的结构就是一颗结构树,例如上述的一个卡片在代码中是这样拼接的
<View style={styles.box}>
<Img src={imgUrl} width={350} height={350} />
<View style={styles.attrbox}>
<Attr attr={attr} />
</View>
<Title isBest={isBest} shili={shili} width={310} title={title} style={{marginLeft: 20, marginRight: 20}} />
<Tag style={{marginLeft: 20, marginRight: 20}} services={services} />
<SaleBase priceIcon={priceIcon} price={price} oldPrice={oldPrice} activeStatus={activeStatus} quantityBegin={quantityBegin} showUnit={showUnit} unit={unit} style={{marginLeft: 20, marginRight: 20}} />
<Row top={14} bottom={20}>
<Btn leftUrl={leftUrl} rightUrl={rightUrl} ticket={ticket} btnText={btnText} activeStatus={activeStatus} />
</Row>
</View>
我们需要将我们上述的物料组装成一样的树形结构,还原成上述的结构树,形如

在拖拽的过程中,就是组装结构树的过程,除了结构以外,我们还需要设置节点的样式和布局,在这里过程中,可视化的便利性的得以展现

通过拖拽的方式快速实现UI排版,有的开发同学可能会说,我前端代码写的贼6,css写的快速飞起,你拖拖拽拽就可以比我写的快吗?从我们200+的组件低代码开发经验来看,可视化的开发平均效率比传统开发快3倍左右
设计稿导出UI
除了可视化开发UI以外,我们还有更加激进的方法,就是将视觉稿直接转化成UI,借助集团提供工具imgcook,我们可以快速将视觉稿导出成UI

缺点就是设计师在设计视觉稿的时候,需要有一定的规则,这些规则只要设计师稍加注意就行,在后续设计师遵守这个规则下,我们可以快速基于这个静态UI做二次加工,数据绑定等后续开发,大幅降低UI开发时间。
线上物料池
上面我们说了一个问题,就是基础组件碎片化,碎片化的原因就是没有一个集中的中心组件池基础,因为团队成员都是线下各自开发,沟通协同分散,要解决这个问题我们一般都是集中进行管理起来,然后通过文档、统一站点等入口来让开发成员都以一个基础组件池为标准进行迭代,我们通过低代码平台来讲述另一个方法:线上物料池,通过这种方式能更有效的收拢基础组件。
首先我们还是要有一套基础组件,然后我们将这些基础组件通过组件协议注册到线上,形成一套基础物料池,如图

任何开发者只要使用了我们这套低代码平台,必定会打开这个面板进行选择,所以物料池是 唯一卡口 同时我们在将基础组件注册到平台的时候,会附带一个组件协议,用来描述这个组件有那些属性可以用,例如文本组件
{
name: 'content',
title: '文字内容',
supportVariable: true,
display: 'block',
setter: (
<TextSetter
multiline={true}
rows={10}
maxLength={null}
pattern={null}
placeholder='Hello World'
/>
)
}, {
name: 'numberLines',
title: '行数',
display: 'inline',
setter: (
<NumberSetter
placeholder="请输入行数"
min={0}
max={100}
/>
)
},{
name: 'isEllipsis',
title: '是否省略',
display: 'inline',
initialValue: true,
setter: (
<BoolSetter />
)
},
在属性编辑器中,我们就可以看到对应的内容

属性的编辑可以在左边的预览中所见即所得,省去研究组件文档的时间
实现原理
上面说了这么多,那我们来讲下上述这些的实现原理
节点编排交互

Pages对象,则保存了对一系列Page对象的引用,提供了管理一系列 Page 对象组的能力。Page对象封装了当前页面所有的数据结构,也是页面Schema解析之后形成的对象,它封装了所有的页面信息,并保持对当前页面「根节点」,即RootNode的引用。Node节点,包括根节点在内,是对一个功能区块或者一个「组件」的抽象,具体和「设计视图」中的组件树上的各个组件相映射,存储了表示组件的数据结构,封装并提供了一系列针对组件的,比如:修改、编辑、保存、拖拽、复制 ... 等操作 API。同时保持对组件属性集合Props对象的引用。Props对象描述了当前组件所维系的所有可以「设计」的属性,提供一系列操作、遍历和修改属性的方法。同时保持对单个属性Prop的引用。Prop为属性对象,映射到一个具体节点的具体属性之上。提供一系列操作属性变更的 API,在属性发生变化的时候,会冒泡通知到Props以及Node节点,并在高层次上实现代理做统一的处理和操作。
以schema为核心的低代码协议

我们设计了一套schema协议将设计状态和渲染状态连接了起来,让平台可以通过用户编辑的schmea快速渲染出想要的组件。我们在schema是这样描述的

我们会在schema中记录所有节点的信息,包含属性、样式、节点结构等
低代码设计
既然我们叫低代码,那说明我们使用的时候还需要写入代码。我们在设计数据请求的时候,发现我们再怎么设计请求的模型接口,回调函数,入参调整 以我们目前后端的技术体系,还无法做成统一化,所以很多时候我们要针对不同的业务场景做一些处理,这里我们就引入了低代码模块。例如数据源回调

通过微软的 monaco-editor 我们可以快速构建属于自己的轻量WebIDE,同时拥有简化版的VS-Code开发体验,用来开发函数级代码非常顺畅
代码有了,那我们怎么植入到模块?
首先我们想编写的es6模块,通过babel在线转化成es5,并存入schema
"onSelect": {
"type": "js",
"source": "function onSelect(ctx,index,itemData) {\n console.log(itemData.categoryId);\n ctx.dp.setValue(\"categoryId\", itemData.categoryId)\n}",
"compiled": "\"use strict\";\n\nvar __preParser__ = function onSelect(ctx, index, itemData) {\n console.log(itemData.categoryId);\n ctx.dp.setValue(\"categoryId\", itemData.categoryId);\n};",
"error": {
}
},
渲染的时候,通过 new Function() 并注入变量就可以执行了
转码
我们把所有组件的信息全部放置在schema中,那我们实际的代码是如何产出呢,这里就需要将schema转化成代码

通过babel插件,我们可以将schema还原成手写的代码,做到了只需要可视化操作+少量代码片段,就可以完整的生成一套组件源码,大大提升了开发效率。
以上是我在1688移动端业务中的低代码使用探索,后续我们会增大覆盖面,将PC、小程序开发也一同加入进来,通过可视化的方式,降低开发成本,持续降低前端人员在流水线开发中的投入,把这个劳动力降至最低。未来的开发链路的不断整合,开发的低代码化、甚至无代码化、AI智能化逐渐成为趋势,希望和大家一起共同探索这个领域。