这篇文章中提到的内容,均为我今年在蚂蚁集团·体验技术部 4 个月暑期实习的成果,在此特别说明。非常感谢实习期间 

@林外

@偏右悄悄地

@王嘉喆 等前辈们的指导,以及诸多可爱的小伙伴们,想你们呀 XD。



自诩为「设计工程师」的我,常常一手做设计,一手写代码,因为常常这般这样站在设计与开发的十字路口,往往能够看到或者碰到一些有趣的东西。在体验技术部的四个月的实习中,我在 C2D 领域做了一些探索和成果,通过这篇文章进行一下总结,并和大家做一下分享,抛个砖,引个玉。

 

设计工程化与C2D

设计工程化

关于设计工程化这个概念,大致应该可以追溯到「设计规范」那个时代,在「设计系统」概念发展起来时开始展露头角。其中 Airbnb 可以说是这个领域的先行者,知名的 Lottiereact-sketchapp 等都他们的探索产物。他们甚至发明了「设计工程师」这样的 title,用于称呼专注在设计工程化领域的设计师们。
如果你对设计工程化不太了解,可以看一看《设计的工程化》这篇文章获取相关的背景知识。这篇文章里, Codesigner 很好地介绍了设计工程化的背景、意义和方式,所以这里不再赘述。
我个人认为「设计工程化」是设计领域在未来的一个重要趋势。Airbnb 团队的首席设计师 Alex Schleifer 曾说过「要想创造出创新产品,请先创新你的创造方式」,我想正是这样的思想才能带来像 lottie 这样的提效工具,甚至往后发展出了相关的 lottie 社区。
如果你对设计工程化或者很感兴趣,也可以看看下面一些文章进一步了解相关知识:

C2D (Code to Design)

C2D 是设计工程化当中的一个有意思的分支,关注于如何将代码转换成设计。有人会问,我们为什么需要把代码转成设计?这有什么意义和价值吗?当然有,而且还挺多的。例如保持设计系统的一致性、设计系统的自由拓展等等,下文会详细展开讲讲。
当然,与 C2D 相对应的也有 D2C(Design to Code),关注如何将界面直接生成代码。关于这个领域的探索,无论是在学术界还是产业界也非常多,但是这不是本文的重点,所以不做展开。

 

C2D 先行者之殇


正如上文提到的C2D 的价值,所以有不少人在这个领域进行研究和探索。比如 react-sketchapp 和 html-sketchapp ,是在业界比较知名的探索方案。但是他们都或多或少存在一定的问题,也算是“先行者之殇”吧。

react-sketchapp


react-sketchapp 的思路是 sketch 作为 react 的一个端,通过类 react-native 的语法,使得开发者可以用 react 语法渲染出 sketch 文档。换一种性感一点的说法就是:“用写代码的方式来做设计”。


这种理念虽然比较超前,但是具有明显的局限性:

  • 从传统软件开发流程来看,既然代码都能写出来了,那我们何必还需要再输出 Sketch 呢 ?毕竟设计文件在开发流程中只属于中间产物;
  • 如果让设计师来用,会写代码的设计师太少了,这种模式不太可能进行推广,尤其是 react 的学习门槛也比较高;
  • 而如果让开发者来用,由于 react-sketchapp 的语法要求开发者必须以 react-native 的方式进行开发。这就使得开发者脱离了熟悉的 css 写法(react-native 必须采用 css-in-js 的方式写样式),更糟糕的是,如果为了输出 sketch 文件,那么开发者必须要输出两套代码(一套用于网页版的 react 代码和一套用于 sketch 的 react-sketchapp 代码),这对开发者来说几乎没有收益。

所以 react-sketchapp 只是一种符合 Airbnb 团队内部环境的基础设施,不会成为设计与前端协同的一种趋势,但是可以说是开启了 C2D 的探索大门。

 

html-sketchapp


相比于 react-sketchapp, html-sketchapp 脱离了框架限制,可以直接将任何框架开发的 HTML 页面转换成 Sketch 文档。

动图封面
基于 html-sketchapp 开发的命令行网页转换工具


这无论对于开发者还是设计师来说,都是一个好消息。

  • 从开发者角度来看:对比 react-sketchapp 绑定框架的做法,html-sketchapp 不限制框架,这使得开发者可以使用自己熟悉的框架进行开发,开发体验得到了极大提升。同时对比 react-sketchapp 需要维护两套代码的情况,html-sketchapp 可以直接使用上线版本的代码,一键获得 sketch 文档。从开发者的角度来看,几乎没有添加开发成本,但是能够带来额外的收益;
  • 从设计者角度来看:由于html-sketchapp 可以解析任何框架实现的网页,这就带来了一种可能性:任何网页中的任何元素,都可以直接变成 sketch 文档供设计师使用,目前肉眼可见的相关项目有:html-sketchapp 的示例html-to-sketch-electron 等。(虽然这两者都做的比较烂,用户体验很差就是)

虽然 html-sketchapp 具有如上优势,但是他也具有明显的局限性:

  • 不支持伪类、文本溢出(overflow)、渐变、transform 等属性。由于网页在实现上非常自由,这就使得很多网页使用 html-sketchapp 解析出来后缺胳膊少腿,视觉还原度不高;
  • html-sketchapp 虽然不依赖框架,但是与 sketch 完全耦合。html-sketchapp 实现的原理,是通过解析 DOM 树的节点信息(比如位置、尺寸、节点之间的包含关系、样式等等),然后将其转换为符合 Sketch 文件结构的对象,最后再利用 Sketch 内置的导入 JSON 方法获得 sketch 文件。但是由于 html-sketchapp 的生成的 JSON 并不严格符合 Sketch File Format ,所以无法直接用这个 JSON 对象生成 sketch 文档。导致这个问题的根本原因,是因为 html-sketchapp 依赖了 Sketch 和 macOS 的部分解析能力。(个人感觉是这个开发者直接偷懒了) 例如 html-sketchapp 解析网页中的 Svg 对象只会解析出 svg 字符串,不会变成 Sketch File Format 中的 Shape 对象。在使用该框架配套的 asketch2sketch 插件导入 JSON 时,该插件会调用 Sketch App 内部的 SVG Importer 方法将 svg 字符串转换成 Shape 对象。这个时候的 JSON 对象才是符合 Sketch Format 的对象,可以用导入方法导入。

由于上述原因, html-sketchapp 生成出来的 JSON 对象必须依赖 Sketch App 的解析方法才能转换成合法的 sketch 文件。这就会导致我们无法利用 html-sketchapp 在服务端生成合法的 sketch 文件(除非有 macOS 的服务器),从而失去了诸多自动化的可能性。

  • html-sketchapp 使用 js 开发,缺少完善的类型定义,项目组织架构不太合理。这个是不算功能缺点的缺点,毕竟 2020 年了大家都用 Typescript 了吧,类型定义的完善才能够带来足够好的开发体验。我曾试图在这个模块的基础上进行功能优化,但是由于该项目缺少了类型定义以及组织架构不太合理,导致我的整个开发体验极度痛苦,遂作罢。

 

antd-sketchapp

在这里也可以提一下 Ant Design 团队基于 react-sketchapp 的实践:antd-sketchapp


这个库出现的原因大致是 Ant Design 团队希望基于 react-sketchapp 框架,构建一套可以给 sketch 使用的 Ant Design 组件库。从而将设计系统的 sources of truth 锁定到代码层。

动图封面
基于 antd-sketchapp 开发的 Sketch 插件

当然,想法是美好的,现实是残酷的。
由于 react-sketchapp 并不支持直接引入网页的模块进行渲染。所以 antd-sketchapp 的所有组件,必须 *重*头*开*发*。这简直要命,所以可以看到这个项目很快就不再开发,转而进入维护状态了。

 

html2sketch 做了什么?

陈述完上述两个先行者的优劣,接下来将介绍一下我在过去半年内的一个重要工作成果:html2sketch 。


html2sketch 是一个可以将 HTML 解析为 Sketch JSON 的模块。它借鉴了 html-sketchapp 的思想,并从底层开始重新开发。

接下来讲讲 html2sketch 做了什么以及为什么这么做。

 

解析能力强化

html2sketch在原本 html-sketchapp 的解析能力基础上, 还支持了 html-sketchapp 不能解析大部分的网页样式,比如伪类、渐变、文本溢出等等。这一点对于网页的视觉还原效果来说,非常重要。可能原本使用 html-sketchapp 能够还原出 80% 的效果,而在使用 html2sketch 模块之后,可以支持到 95%乃至以上的还原度。
在这里不得不提一嘴。由于很多网页在代码实现上非常自由(说真的,一百个开发者真的会有一百种样式的实现方法),我花费了巨量的精力在解析能力的覆盖上。有的时候解析出现不正确,往往就是因为某个开发者用了一种奇葩乃至称得上鬼才的写法。
比如 antd 有个 Checkbox 组件,它的选中状态如下:


从一个设计师的视角来看,这个勾应该是个 svg 对象吧,当时 html2sketch 是可以解析 svg 的。但是这个白色的勾就是死活解析不出来,然后我一检查,看到了下面的代码。


好家伙,这个勾居然是一个伪元素?

卧槽,这个勾居然是两个描边(右描边、下描边)模拟出来的?

然后,transform 是什么鬼?

嗯?还特么转了 45 度?

看到这,我整个人都快炸裂了。


我想这个组件的开发者实现这个样式的时候一定很兴奋,老得意了,看我写的代码多么巧妙多么优雅,都不用插 svg 了!然而类似这样“巧妙”的代码却不知给我做解析带来了多少麻烦。(我还专门为此去学了矩阵你敢信?)

虽然最后解析是可以解析出来,但是转换到 sketch 中的实现结构说实话有点惨不忍睹…由于 sketch 没有单侧描边,所以只能用内阴影来模拟。最后的结果就是下面这样…

这个很小的 case 也从侧面反映了,目前设计和开发之间的鸿沟,是有多么巨大。


当然,吐槽归吐槽,html2sketch 的解析能力也需要靠解析场景的覆盖来逐步提升,如果在使用当中遇到不正确的解析情况,请务必给我提 issue ,我会尽快解决。目前该模块支持的样式都罗列在 解析用例 中。

 

与 Sketch App 解耦

html2sketch 这个模块在功能层面最大的差异点(也是工作量最大的部分),就是将不再使用 Sketch App 的解析方法。相关的解析能力 (Svg、图片、字体等的解析)全都从零开始构建。
由于 html2sketch 生成的 Sketch JSON 将严格符合 Sketch File Format ,所以在获得这个 Sketch JSON 后,就可以直接使用 sketch-json-apinode-sketch 等模块合成合法的 sketch 文件,不需要其他任何处理。当然,你甚至可以利用一台普通的 linux 服务器来解析网页,生成 sketch 文件(目前已经用于我们团队内部的生产中)。

 

采用现代化的技术栈

html2sketch 还采用了其他现代化的开发技术栈,保证开发体验:

  • 采用 Typescript 作为开发语言:众所周知,TS 的类型提示对于开发体验的提升是无与伦比的。html2sketch 从头到尾都采用 TS 进行开发,所以无论是作为模块消费者来使用,还是作为模块开发者去生产,和 html2sketch 打交道的体验都将十分良好;
  • 采用 dumi 作为开发框架:dumi 是一个帮助开发者专注模块开发的框架,利用该框架可以保证模块的开发、打包与部署效率,提升文档撰写体验;
  • TDD 驱动开发:每个解析模块都完成了单元测试的覆盖,代码覆盖率目前 89% ,测试用例数达 300+,从而保证功能稳定性,提升功能开发与重构的体验;
  • 各模块注释完善:由于本人并非专职前端,水平也有限,职能切换容易导致思维混乱,所以个人习惯将思考过程以注释形式记录在代码中,因此也使得该模块的注释和文档说明相对完整。

 

html2sketch 可以用来干嘛?

html2sketch 本质上一座桥,沟通了网页和 Sketch端,因此就带来了很多有意思的用法和可能性。比如我已经探索出来的网页一键粘贴、复杂组件智能生成等等。

网页一键粘贴


正因为 html2sketch 可以获得合法的 Sketch JSON 文件,那么基于这样的能力,我开发了 Sketch JSON 插件。该插件可以将合法的 Sketch JSON 文件转换成 Sketch 内部的对象,从而实现将网页一键“粘贴”进 Sketch。

请看演示:

动图封面
直接复制网页对象

当然,想要 Symbol 也可以:

动图封面
直接生成 Sketch Symbol


如果感兴趣,欢迎尝试:

 

Microwave:表格智能生成

Microwave 是我开发的一个 Sketch 插件,功能聚焦在复杂业务组件(如表格)的配置、一键生成与修改

Hello Microwave · 语雀​www.yuque.com/design-engineering/microwave/xd6hez


基于 Ant Design 的 ProTable 组件配置格式作为数据交换对象,利用 html2sketch 作为转换桥梁,从而实现表格的一键配置生成, 乃至到代码的一步转换。

Microwave 表格生成 & 开发对接全流程演示64 播放 · 1 赞同视频​

Microwave 的项目地址如下 ⬇️ ,网页版的体验地址在这:➡️ 传送门 。欢迎感兴趣的小伙伴关注:

Microwave · 语雀​www.yuque.com/design-engineering/microwave


不过由于最近一段时间精力有限,暂时没有更新,可能要等我忙完硕士论文,再进一步发展。

 

设计系统的自动化

由于 html2sketch 实现了 C2D 的设想,所以自然而然的,就带了和设计系统相关自动化能力,例如:

  • 保持设计系统的一致性:如果能够将代码完美还原为设计,那么我们就可以轻松保持设计稿与线上版本的一致性,不会出现线上的版本更新了,然而本地的版本没有修改的情况;
  • 轻松拓展设计系统:由于现存设计工具的局限性,我们无法很便捷地实现设计系统的轻松拓展(比如间距、圆角的修改就会让人头疼到炸裂)。而在代码层面,任何视觉元素都可以自由定义成 design token 进行复用,无论是颜色、字号、圆角乃至间距、行高等等变量,全部都可以 token 化。一次修改后便可以全系统响应,省时高效;

关于这部分的能力,目前已经集成在了团队内部产品新海兔中,说不定未来就会和大家见面 XD。

 

One More Thing…

DSL 与领域模型

在做 html2sketch 的过程中,我与 Sketch 的文件结构打了无数次交道,逐渐感觉 Sketch 的文件结构过于繁琐。存在了大量无用的冗余信息,比如存在判断对象是否需要转换的参数 needsConvertionToNewRoundCorners 和 hasConvertedToNewRoundCorner (看到这两个参数时我整个人都快裂开了,Sketch 的数据结构设计水平有点感人)。这也促使了我开始思考将其优化的可能性。
从本质上来说, html2sketch 在做的事情,就是将描述 DOM 的数据结构转变成描述 Sketch 文件的数据结构。结合过去的学习经历、开发经历,我在某个时刻自然而然地想到,是否能有一种数据格式,与平台无关,只用来描述设计对象。如此一来,我就不用过多的考虑某个端(在这里我把网页、Sketch 都当成一个「端」来看待)的结构,只在需要的时候将其转换成相应的数据格式即可。


而这样的数据结构将足够凝练,只用于描述设计本身,一切都会变得非常简约。而我也是在那个时候了解了 DSL (Domain Specific Language,领域特定语言)。就像 html 和 markdown 之间的关系一样,html 是一种用于描述网页结构的xml 类语言,所以存在很多标签形式。这对于机器来说无所谓,但是对于人来说就有很大的语言噪音。而作为人来说,明显 markdown 的形式比起 html 更便于我们进行书写和阅读,那这就是 DSL 设计出来的意义——能够提升我们在该领域的效率。


于是,我开始了解到我所设想的概念其实就是领域模型(也可以称之为业务实体)。也因此有感而发,写了这篇文章:空谷:DSL 与界面设计杂谈
在做 html2sketch 的时候,也曾有同事和我聊起,说这件事情的性价比可能不高。原因是介于 Sketch 团队的不思进取,在可预见的未来,Figma 一定会取代 Sketch 变成主流的设计工具。而到那个时候,我现在做的这一切都会变得没有什么用了。


但是我当时已经想明白了领域模型的概念。所以聊起这个话题,我反而觉得倒还好,因为除了 html2sketch 这个模块本身,在它背后的蕴含的领域模型价值其实才是最大的。因为弄清楚了领域模型,就意味着抓住了这个领域的本质,而在这个领域长出来的所有东西,都是附在上面的皮而已。


所以,在我的构想里 html2sketch 的底层一定,也必须是一个被准确抽象的领域模型(即传说中的「设计意图」)。而从领域模型的视角去看,切换到 Figma 无非就是多做一个端适配的事情而已。


而一旦抽象出了这个领域模型,任何端之间的变换都能够打通,比如从 sketch 导出到 Figma,或者从 Figma 导出到 Sketch,甚至从Sketch 导出到网页中进行二次编辑,再导入 Figma,这一切设想都将可以基于一个统一的设计领域模型实现。

而这件事,只可能由设计师亲力亲为。

因为只有设计师才最懂自己的这个领域,如果由程序员来思考这个的模型,就有很大概率出现问题。

例如 Sketch 和 Figma 的描边:


Sketch 和 Figma 都不支持单侧的描边能力,原因就是他们的数据结构中都没有提供任何记录单侧描边的字段。这就导致了功能的缺失,以至于我们现在所有的单边描边,要么是手画一根线,要么就是用内阴影来模拟单侧描边,使用体验都不太好。

Sketch 描边数据结构
Figma 描边的数据结构

由于软件的数据结构往往是基于领域模型进行设计的,所以我们可以说无论是 Sketch 还是 Figma,他们在设计的领域模型构建上都存在缺陷。 Sketch 和 Figma 更新了那么多年那么多版本,连这个这么基础肉眼可见的问题都不处理,我只能说他们的领域模型肯定不是设计师构建的,才会留这么大的坑不填。
为了抽象设计的领域模型,也为了了解 Figma 的数据结构,我花了点时间将 Sketch 的数据结构和 Figma 的数据结构放在一起进行了对比。

相比起来,Sketch 在「组件」、「布局」层面的数据字段明显比 Figma 少很多,而 Figma 在这几个方面的数据结构设计明显比 Sketch 要合理不少,这就意味着 Figma 在这几块的领域模型抽象的比 Sketch 好很多。相信这也是越来越多人会说 Figma 才是未来的原因之一。
上述这些工作这让我深刻地意识到:设计工具可能会被取代,但是设计的领域模型永远不会被取代。
设计工具的更替过程,本质上就是人们选择操作领域模型更高效的工具的过程。
设计工具的选择过程,本质上就是人们发掘描述业务更合理的领域模型的过程。

铁打的领域模型,流水的设计工具。


所以,我们必须从领域模型的角度出发思考和分析问题,才能立于不败之地。

 

UXDM

在实习期间没有机会做,终于借着毕业论文的机会,我准备把这件事情尝试落地——提炼设计的领域模型

而这个模型,我称之为:UXDM(User Experience Description Model)——用户体验描述模型

UXDM 的目标是能够精准描述出「用户体验」的领域模型,并能完整、精炼地描述用户体验,且与语言、工具无关。
为什么需要有 UXDM?

在我的实践中,我发现原因有二:

  1. 前端实现 low-level:文档对象模型 (DOM) 过于偏向浏览器实现,抽象不足,技术细节过多,并不适合描述用户体验;
  2. 设计界面 low-level:Figma、Sketch 等设计工具过于具象,其数据结构不具有通用性,又具有各自的缺陷,没有办法用统一的方式来描述用户界面,且无法表征界面元素包含的交互行为。

所以我们需要有一种相对 high-level 的模型来描述设计意图,表达界面和交互。


从目前前端的发展来看,React 其实是一种比较不错的 DSL参考对象,但是 react 问题在于它是开发层面的,仍然包含大量开发相关的属性。而 UXDM 最好是纯粹的领域模型,不包含任何实现层的细节。有了这样的模型,我们就能够实现更多的可能性,比如形成设计与开发的协议、更轻松的 C2D和 D2C,乃至一门类似 Markdown 的用于描述设计乃至用户体验的语言(暂且称之为 UXDL 吧~)。

从抽象的层面来看,UXDM 应包含两个「UI」:

  • 第一个 UI 是 User Interface , 即用户界面;
  • 第二个 UI 是 User Interaction,即用户交互。


目前,我还处于解决第一个 UI 问题的阶段,第二个 UI 的问题还没有太多思路。期待未来更多的实践能够给我带来启发。当然,如果你对这个项目感兴趣,也欢迎关注 UXDM 的项目文档,或在 Github 上关注~

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。