Deco 智能代码技术揭秘:设计稿智能生成代码
1、研发提效还可以怎么做
研发效能的提升一直是我们追求的主题,从最初的工具化到工程化,工程师们尽其所能去实现更快速地书写代码,来应对不断增长的业务需求,而后,小程序等各类平台的崛起,工程师们又开始研究多端统一开发的解决方案,让我们可以一次性写出跨端运行的代码,进一步提升效率。但个性化的业务依然还在爆发式增长,那我们不禁要发出疑问,我们要如何继续进行革新,来提升我们的研发效率。
我们思考「求变」,在智能化思想愈来愈热的当下,传统的研发提效方式遭遇瓶颈,那我们是否能用智能化的思想来解决呢?我们思索着,既然更快地写代码这条路看似已经走得差不多了,那我们能否基于 AI 手段来实现代码生成,让我们写更少的代码,于是,我们探索的方向就转向了「前端」+「智能化」,希望借助 AI 和机器学习的能力拓展前端能力圈,打通设计与研发的工作流程,实现规模化生产。
2、智能代码——被选中的道路?
Deco 智能代码项目是我们团队在「前端智能化」方向上的探索,我们尝试从设计稿生成代码(Design To Code)这个切入点入手,对现有的设计到研发这一环节进行能力补全,进而提升产研效率。
在一个日常需求开发流程中,往往需要遵循固定的一套工作流程,产品提交需求 PRD,交互设计师根据 PRD 输出交互稿,再由视觉设计师输出产品视觉稿,接着再进入前端开发工作流。对于前端工程师来说,输入源是视觉稿 + PRD,输出结果是可上线的页面代码。
Deco 期望解决的是上述流程中,对于前端工程师而已相对低价值,以及可用复用思想处理的工作:
UI 视觉稿还原,即页面重构,编写 HTML + CSS;
可复用的业务逻辑绑定;
设计稿中已有组件的识别与替换
以「设计稿生成代码」为切入点,我们探索利用智能化的解决方案来替代传统的人工页面重构(分析图层样式+切图等),期望能从视觉稿原始信息中提取结构化的数据描述,进而再与各类智能算法结合,输出可维护的页面代码。
Deco 经过 618 大促的初步验证,随后不断升级打磨,在正在火热进行的双 11 个性化会场研发中已经广泛投入使用,覆盖 90% 左右的大促楼层模块,为业务研发带来 48% 左右的效率提升。
图 3 双 11 部分个性化会场及模块
3、如何实现一个设计稿生成代码方案
3.1、生成静态代码
设计稿智能生成代码的第一步是生成静态化的代码,而这一步的核心是如何根据设计稿生成一份「结构化的数据描述」信息,这份数据称为 D2C Schema。
Deco 设计稿智能生成静态 代码主要做了两件事情:
从视觉稿中提取「结构化的数据描述」;
将「结构化的数据描述」表达成代码;
本质上,Deco 智能代码是通过设计工具插件从视觉稿原始信息中提取 D2C Schema,然后结合规则系统、计算机视觉、智能布局、深度学习等技术对 D2C Schema 进行处理,转换为布局合理且语义化的 D2C Schema JSON 数据,最后再借助 DSL 解析器转换为多端代码。
3.1.1、处理设计稿
一份 Sketch 文稿是由若干图层元信息(分为 Document 和 Pages 等)和资源文件(主要是图片)组成的一个压缩文档(文件后缀为 ".sketch"),我们需要通过对图层元信息进行加工处理后得到一份供布局算法服务处理的数据。
通过开发 Sketch 插件,使用 Sketch 提供的 API 来帮助我们去操作 Sketch 文稿,拿到图层信息后,对这些数据加工、筛选等处理。图层信息的处理主要是分为两层:
设计稿加工层:将设计稿中的 Symbol 解耦成实际图层,然后再对图层进行各类处理,如过滤不可见图层,合并必要图层,处理蒙层等等;
图层信息处理层:提取图层中有用信息,将信息进行转换,同时去除掉无用图层,打平图层信息等等。
下图是对图层信息的处理流程:
除了对图层信息的基础处理之外,我们建立了一系列的数据导出的优化规则,用于增加布局以及语义的合理性。比如在一些大促设计稿上,复杂背景图的设计可能是在一个图层组下由若干个矢量图形组成(如下图),如果原封不动地将这些图层导出,会给布局带来很多复杂度和不确定性。
在合图的这一流程中,针对一个图层组下所有图层都是矢量图形的情况,我们会将它合成为一张图片,这样会大大减轻布局的困难度。最终合图效果如下图:
当然,上面提到的这些优化规则并不能满足所有的情况,毕竟设计师是自由的。为了提高布局和语义的合理性,我们对入参的设计稿提供了一些规范协议供设计师以及开发者使用。
3.1.2、通过布局算法还原设计稿
设计稿插件处理好的数据需要经过布局算法处理,来获得视觉还原度良好、布局结构合理的代码结构表达数据。
经过插件导出的元素数据,都是以左上角 (0, 0) 为坐标原点坐标的绝对定位为基础的元素信息,并且在一般情况下(无主动编组、无 AI 识别等等情况 )元素都是扁平化的,也就是元素间没有从属关系。
在前端开发过程中,绝对定位布局无论是扩展性、可读性都达不到开发要求,那么如果不解决,就成为 一次性代码 。因此,需要布局算法来提高生成代码的扩展性、可读性,供后续二次开发使用。
布局算法层的操作流程包含三大步骤:数据结构转换、布局推导、样式计算。
数据结构转换是将 Schema JSON 数据转换为类似 DOM 树的结构,可以进行节点插入、删除、查找操作。
在数据转换处理之后需要进行布局推导,在这一步进行行列分割推导,总体上包含:空间布局算法、投影布局算法、背景图布局算法、特征检测布局算法、坐标推导算法、背景图层及冗余图层检测算法等等。在布局推导之后,Layout 结构已经有了明晰的层级关系及相邻关系,我们就能获得分割良好、成组合理,结构化的节点结构。
处理好布局结构生成之后需要进行样式计算,是对经过布局推导层得到的结果进行一系列的计算,例如,基于层级关系,可以通过坐标计算得出 Flexbox 主轴、侧轴;基于相邻关系,可以计算出相邻之间的 margin 等等样式。Deco 样式大部分布局采用 Flexbox,有些特殊情况需要使用绝对定位。
经过布局算法处理之后,我们就能获得还原性良好,结构合理的 Schema JSON 数据。
3.1.3、生成语义化的代码
当设计稿数据经过布局算法处理后我们就能获得结构较为良好的代码,但此时我们会发现由于节点元素缺乏相应的语义化类名,代码依然不具备很好地可读性。为了最终能得到可以二次开发的代码,我们需要在布局算法之后加入语义化处理来让代码拥有良好的语义性。
语义化首要解决的问题就是如何为元素节点加上具有语义化的类名。
为了实现这一目标,我们可以先回顾一下在我们开发的时候是如何给元素节点加上类名的,以如下的单个商品图为例。
上图是一个商品图的示例,我们会通过图片、价格、图片下方文案等因素来判断出这是一个商品,然后我们就可以给这一个区域赋予类名 goods
,而区域内的节点,比如图片可以赋予类名 goods_pic
,图片下方文案可以赋予类名 goods_tit
,价格可以赋予类名 price
,这就是我们为元素节点添加类名的一般逻辑。
可以看出,通常我们去确定一个区域,一个组件的语义时,我们需要依据区域内节点的语义组合才能进行判定,比如上面的商品组件,需要依靠内部的图片、价格、文案等元素才能确定语义,从而确定类名。因此,语义化的处理方式,就是从容器元素的子节点出发,先确定子节点的语义,然后再推断出容器元素的语义,一层层往上进行推断,最终推断出整棵节点树完整的语义。
在语义化处理中,我们主要的处理对象就是经过布局算法处理后的 JSON Schema 数据,我们称之为布局树,此时布局树已经具备了良好的结构,我们可以对它进行语义化推断操作。推断的流程就是从树的叶子节点出发,一层层向上冒泡到枝节点,最后再冒泡到根节点。
目前我们进行推断的依据主要是节点的位置、样式、大小、兄弟节点等因素,同时会结合不同节点的类型,组合一些智能化手段进行辅助推断。例如,最小叶子节点一般可能为图片、文本两种类型,针对文本我们可以通过 NLP 的方式去分析文本的词性、语义;针对部分图片,我们可以使用图片分类或识别的方式确定图片分类或者提取图片上的关键信息进行图片的语义判定。
为了确定每个节点的语义,我们需要组合一系列的规则对现有的事实(样式、位置等信息)进行推理,而同时,经过一些规则推理后又会得到新的事实,又需要经过其他规则推理之后才能得到最后确定的结果。所以,这是一个基于规则推理的推理系统,我们可以通过实现一个正向链的推理引擎,来帮助我们进行推理决策。
例如,推断上述商品组件的过程,首先我们先找到具备价格因素的文本节点,命名为 price
,然后我们找到 price
附近,在树中所处层级相近的图片节点,并且该图片节点符合商品图大小的要求,这样我们就能基本确定同时包含价格和符合商品图特征的容器为商品容器,再根据容器中元素个数,图片附近是否有一段文本,以及对文本的 NER 分析,我们就能确定这段文本是否是商品名,从而确定其语义化类名。
在整个语义化处理中,上述的判定规则只是冰山一角,我们结合整个电商场景,分析了大量设计稿与线上案例后总结了大量的判定规则来帮助我们进行合理化语义命名,同时在语义化过程中采用 NLP 分析、图片分类及识别等 AI 手段,来协助获得更加精准的语义化名。
3.1.4、生成 DSL
经过前面几步处理之后我们就能进行代码生成了,为了支持生成不同 DSL 的代码,我们将语义化处理后的数据统一抽象成类 Virtual DOM 的节点描述数据,然后再将节点描述数据处理成各类 DSL 代码,同时预留好接口,支持拓展新的 DSL。
3.2、让代码拥有灵魂
在实现生成静态代码之后,我们会发现有些时候设计稿中会出现已有的组件,最好的方式是我们能够识别出设计稿中已有的组件,然后在生成代码的时候进行复用。
为了解决这一问题,我们将目光投降了深度学习,利用深度学习中的目标检测与分类算法,我们就能对设计稿中的已有组件进行识别、定位,并最终映射为组件库中的组件,实现组件复用的目的。
组件识别映射整体处理流程是,针对输入的设计稿图片,先基于视觉算法进行合理切割,在对切割后的图片识别出图片中包含的 UI 区块,然后将 UI 区块映射到 Schema JSON 中的节点信息中去,而后再对区块通过分类算法识别出最有可能的组件类型,再将识别信息写入到 Schema JSON 的节点中,从而最终实现组件识别映射。
目前,我们已经完成了大促业务组件库的训练,可以精准地识别定位设计稿中的大促组件。但我们发现,如果我们想让其他业务来接入组件识别映射方案时存在诸多困难,其他业务团队不具备 AI 处理能力,如果都让我们来进行组件训练则会极大地增加我们团队的工作量,为了解决这些问题,我们思考如何将 Deco 已经沉淀的 AI 能力进行共享,于是设计了 AI 开放平台。
AI 开放平台为开发者提供了零门槛接入的图片分类、目标识别等 AI 能力,开发者可以在 AI 开放平台上进行自定义数据集创建管理,选用预设算法模型后就能直接进行模型训练与预览评估,这样的话,如果有新组件库需要训练,就可以交给业务团队通过 AI 开放平台来自行操作。
3.3、在业务中进行落地
行文至此,我们已经具备了设计稿生成代码的整体方案,但离方案进行落地实践还有一段距离,我们需要有一个平台来承载我们的方案,当设计稿生成静态代码出现误差时,我们可以通过可视化编辑器进行二次调整,同时在获得静态代码的同时,我们也可以再加上组件化、字段绑定、生命周期等逻辑处理。
3.3.1、组件化
我们可以通过「组件标记」功能,将某一个节点标记为一个组件,标记为组件之后将可以进行设置组件内的状态和数据、设置组件入参、设置组件生命周期等操作,并在最终生成代码的时候以组件形式生成。
3.3.2、数据定义
我们可以根据需要自行定义页面全局数据或者组件内部数据,同时也可以通过设置 React context 来进行共享数据定义。
3.3.3、异步数据请求
针对最常见的异步数据请求的场景,Deco 提供了一个可视化表单,仅需通过简单的配置,即可快速生成异步数据请求的代码。
3.3.4、事件绑定
Deco 提供了包括点击事件在内的多种节点事件,以及组件的生命周期等事件的定义,用户可以在事件中编辑逻辑代码。
3.3.5、属性编辑及数据绑定
此外,Deco 已经提供组件映射的能力,在这基础上,开放了组件的属性编辑和数据绑定能力,实现页面与动态数据的对接。
4、未来展望
如今看来,智能代码是一条非常值得探索的道路,它是一粒种子,也许它此时刚刚发芽,但我们对它期许颇多,我们希望通过 Deco 来探索前端智能化的道路,探索 AI 与前端结合的各种可能性,更重要的是,我们希望能够通过 Deco 开启产研的效率革命,在各种前端工程化、平台、方法论趋于完善的当下,探索为业务降本增效的另一种方式。
同时,我们一直在智能化上苦下功夫,也许在未来,我们可以直接实现设计即交付,那对业界来说无疑又是一场革新。
路漫漫其修远兮,需要技术人上下而求索。
评论