设计稿自动生成可用页面的展望
立足
三年前刚开始做 Sketch 生成代码插件时,定位的就是原型工具,不是可用页面。我当时认为直接由设计稿生成可用代码是走不通的,原因有两个:
- 当时的前端自己都还处在争论用什么框架的时期,得先解决了这个问题设计工具才可能生成被大部分开发者接受的代码。
- 设计语言与编程语言差异比想象中的大。
关于第二点这里详细说明一下。设计稿中的图层通常表达的是离人眼前后顺序,前面的覆盖后面。而图层的分组也只是为了管理而已,没有逻辑上的聚合关系。如下图,背景的红色图层与形状图层是平级的,没有展示出包含关系。
并且在设计中常常会按类型而不是按逻辑归属把一些东西放在同一图层,这样设计师在修改的时候比较容易批量操作。而在前端的组件化中,组件几乎一定是按逻辑功能划分的。而组件的层级表达也是逻辑上的包含关系。这种关系只有一部分能和视觉上的从前到后匹配。以 React 为例,很多组件层级的表达就不是前后关系。
因此,想要实现视觉上到逻辑上的完美转化,只有两种可能,一是按逻辑的要求约束视觉上的图层、分组使用。二是靠机器学习之类的方式去智能识别(社区已有开源项目)。
第一种方式其实技术上可行,但是不人性。两个方面,一是给设计师造成了巨大的学习成本。这个巨大指的不是设计师学不会,而是指规则有可能因为开发者设计得不完善或者不够简便,使得设计师要做很多他自己无意义的额外操作。例如本来设计师一个图层可以画完的元素,在规则的要求下要进行分组分层。二是这些规则很可能忽视了设计师的需求,例如之前所提到的同一图层管理同一类元素的的问题。我有尝试使用社区内的一些方案,从设计师的角度来说,那种体验就像:
第二种方式当时虽然有想过,但是没有深入去探索,原因也是当时的前端变化太快,我认为找不到足够稳定的样本能去训练。
有了以上结论后,我想到了虽然直接生成可用页面不行,但是生成原型还是有可能的。设计师和前端的沟通成本也是一个需要解决的问题。最常见的就是设计师需要告诉前端某个元素 hover 上去是什么效果,点击是什么效果等。这些东西既然设计师已经画出来了,那么能不能把这个表达的方式自动化掉,省去人工沟通的成本。像 Axure 之类的原型工具就有类似的功能。只是这些工具一般都定位给产品做最初的原型设计,缺少了视觉上的细节,不能完全取代设计师和前端的沟通。
因此我开始做从视觉稿到原型的 Sketch 插件 Blade。这个插件虽然也在一定程度上要求设计师按某种规则来设计,但是这种规则对设计师已经是有意义的了,能省去他的口头描述。
旁观
在开发完第一版后,由于工作的调动就没再维护了。后来更是由于 Sketch 升级后的 api 变化导致不能用了。让我比较意外的是这两年间持续不断地有人开 issue,写 email 希望我继续更新。我在 issue 中委婉的表示项目已经不再维护,大家可以试试社区的其他方案。但社区的其他方案屈指可数,并且方向和定位其实也都不太相同。
按我所接触的顺序一一介绍一下。
Marketch,也是生成 html 文件,但是专注于标注功能,方便前端同学取位置和样式信息。这个定位非常精确,让前端不再需要自己装 Sketch。
从 Sketch 的生态体系看来其实插件可以分成两大类,一类是用来方便设计师的操作的,例如快速生成一些常用的元素,如头像、地图等。另一类就是扩展设计之外功能,例如生成标准、原型。对个人开发者来说开发前一类的插件难度比较小,而且收益很明确。而后者相对来说难度和收益就都不乐观了。在后来我再看到的第二类插件中,几乎都是团队进行开发的。例如 Framer 和 Proto.io。
这两个工具的共同点是都已经脱离了 Sketch 体系,变成了独立应用。以导入的方式来整合 Sketch/Photoshop 等工具的产出物。但定位也仍然是可交互的原型产品。直到 Launchpad 出现,才算得上我知道的第一个定位于生成可用页面的 plugin。
从 Lauchpad 所提供的功能看来,生成页面功能非常简单,只有简单的链接、表单,还不涉及到复杂的交互逻辑。生成的代码也不能二次开发。但是 Launchpad 做成了一门生意,并且好像运营得还不错。从跟我进行邮件沟通的设计师中,我也发现了不少想要直接建站的需求。再回过来看我之前认为这条路不能成功的两个原因,反思一下是我想错了吗?
第一个原因是我认为生成的代码必须要前端能接受才行。Launchpad 没有提供二次开发这个能力,似乎就回避掉了这个问题。但同时也就限制了能力。
第二个原因是设计语言和编程语言的差异。同样的,缩小了需要覆盖的场景,不提供二次开发,也使得对设计师造成的负担最小化。
这样看来不是对错的问题,而是本来定位就不一样。我在思考这个问题的时候其实并没有区分场景,定位的是从简单页面到复杂的单页应用应该都能适用的情况。Launchpad 提供了一个新思路,就是缩小场景后能实现一部分。
前面列举的相关的插件都是从设计的角度出发制作的。而不久前 Airbnb 的 react-sketch 插件则提供了一个新的角度。
它的目标是设计资产的管理。它的能力是将组件这种设计资产从代码导入到 Sketch,同时也能从 Sketch 导出到代码。所以即可以说站在了设计师的角度也可以说是站在了前端的角度。在知乎上有一个评价 react-sketch 的问题。前两个回答非常的透彻。读者可以看一看。回答里提到的这种工具在真实团队的适用并不乐观,我有同感。因为它站在了一个新的角度看问题,那么必须也得有一个站在同一角度的新角色(设计工程师?)出现才能显示出它的价值。否则单独对设计师、对前端,都会感觉意义不大。
同样,react-sketch 也提供了一个新思路,就是以设计资产这个维度来实现代码和设计元素的统一是有一定价值的。这种统一,其实也就是能互相转换。
投石
仅仅对 Sketch 插件演变的观察还不足以引发对文题的新思考。真正的机缘来自于最近一两年做框架和可视化搭建平台的经验。仍然回顾最初我认为不能成功的两个原因,或者说两个问题,看看经过这两年,是否发生了新的变化。
顺着上一节讲到的设计资产的思路,我们先看第二个设计语言与编程语言的差异问题。以组件的维度来统一设计与代码,其实本质上是对设计的一种补偿。既然设计师需要遵守统一的约定来使用或者修改组件的设计,不能再按照个人的习惯,那就让这种约定的负担减到最小,即由插件来自动生成,不需要额外记忆。同时将资源的全自动管理作为补偿提供给设计师。用工具来管理可以通过团队的平台进行共享,如果未来整个团队需要升级或者统一的修改,就能通过工具来统一完成,不再需要人肉同步。
再进一步,由于组件本身是一个逻辑单位。虽然设计师在设计师可能不遵从逻辑单位,但是在接受信息,与他人沟通时仍然是以逻辑单位为准的。这样在设计工具上其实可以做更多方便设计师的功能。例如能“快速选择所有按钮、表单、幻灯片等组件的图层进行批量修改该”等。用技术的方式置换掉设计师们以往人肉提升效率的习惯。
这样,通过转换一点点角度,主动为设计师做一些改变,第二个问题看起来是可以解决的。
再看第一个问题,产出什么样的代码才能让前端接受。首先,至少在支付宝内部,前端框架的争论几乎已经没有。大家已经普遍接受了 React 及其生态体系。然而实际编写的 React 代码中,通常原子类型的组件会被包装成相应的业务组件,有业务逻辑。而最顶层也只会看到相应的几个业务组件。不像在设计稿中,所有组件都是原子的,顶层可见的。想到这里时,忽然意识到这也不再是个问题,因为在可视化的搭建平台中,我们已经使用的是一种平铺的,顶层可见的组件结构。只要有合适的应用层框架,在页面结构比较稳定的情况下,它是能解决问题的。如下图中右下角的组件树,表示的就是类似图层的平铺方式。
我们对平铺的组件结构,抽象出了数据树的概念。通过对数据树的操作,可以完美的控制整个组件树,也就是整个应用。而一些上升到应用框架层的高级功能,例如表单的校验等,也是建立在数据树上的。有了数据树,实际上就等于有了真个页面的逻辑入口。下图展示着在调试模式下,用户可以直接看到的数据树。
要进行二次开发,生成的代码不一定要符合手工编码规范,甚至不一定要可读啊!
我们完全可以把生成的界面看做是一个黑盒子程序,只要能保证二次开发者能通过 api 进行控制就够了!(详见前端服务化——页面搭建工具的死与生)。而这种方式所能支持的应用复杂度,其实也就是对应的逻辑编写方式能支持的应用复杂度,不再和界面本身有关。
至此,两个问题都已有了解决方案,在可视化自动生成这条路上算是投出了一个新石子。
絮语
从设计到代码的自动化其实可以看做是可视化平台的最后一块补全。截止到今日,之前的可视化搭建平台已经支持了 56 个系统,在无前端参与的情况下输出了1000+的页面。链路完成了从研发到自动化测试再到上线。测试工具效果如图:
如果成功加上设计链路的话,可以畅想的页面开发场景是:
- 简单的展示类页面能从设计稿直接上线。
- 需要交互逻辑的,由前端在生成的代码上补全逻辑。录制测试用例,然后上线。
- 如果设计有样式上的修改,不影响逻辑,那么设计改完之后能自动重新回归测试并上线,不再需要前端参与。
- 如果有逻辑变化,平台能通过 diff 设计稿得到组件的改动,再通过语意分析得到影响的逻辑代码范围。
搭完这条流水线,就会产出足够规范的样本,或许就能作为足够好的人工智能原料,实现让自己下岗的梦想。
同样,想要参与到这件事的读者可以找我来聊聊 ariesate@outlook.com。
答读者问
最近收到了一些非常高质量的读者提问,摘取部分整理到这里(部分回答也重新进行了修改):
问:
从组件层面的沉淀感觉无法在效率上获得更大的突破了。但是对于功能降级到类似CURD页面的可视化搭建,我都发现有大量潜在的问题,可能归根结底就是“灵活性”和“高效率”之间的平衡。后台类系统最大的问题倒不是展示层的标准化,也不是展示层是如何拖拽产生。 最大的问题就是“界面数据层面以及相关逻辑的错综复杂的关系”。例如:一个日期组件的展示,可能是和前一个分类Select的选择有关,而这个分类Select的值又可能是某个异步接口动态获取,而这个异步接口又可能是其它组件的query拼装而成。 我不排除,构建一个完整的逻辑数据控制流程,并提供复杂的配置界面可以解决这一系列的问题。但如果这种方式带来了超大的配置成本,是不是反而不如直接投入前端人力?传统手写代码的方式,至少开发者面对是自己直接可控的代码,虽然代码量会更多,但更直观也更以维护,而不是一坨黑盒。
答:
在面对真实场景时,确实遇到了“灵活性”与“高效率”权衡的问题。灵活性意味着绝大部分场景倒要考虑到,那必定系统意味要提供各种各样的能力去解决各种各样的问题,同时也就意味着会提高学习成本。在前端框架中 Angular1 的设计其实就是一个例子,他所提供了 service、filter 等各种机制虽然能覆盖足够的场景,但也让学习成本陡升。而我们在搭建这个系统时,提出的方案其实算是绕过了这个问题。我们的方案是:提供一种“渐进式的、易扩展的、程序可读”的抽象,目标是让后端能做前端的事。先解释下方案。
渐进是指,这个系统对于一些简单的页面,让他确实能把页面搭一搭,配置一下组件数据就完成了(很多类似系统都是提供一个“配置数据源”的方式来做的)。而对于复杂的页面,系统又能提供一些其他方式,例如写一些简单的逻辑代码来处理逻辑。简而言之,就是随着用户对用户处理的场景进行一个复杂度的分级,升一级就提供一些新的方案给他解决遇到的新的问题。这里面的关键在于,方案应该是尽量向下兼容的。例如在 http://www.cnblogs.com/sskyy/p/6496287.html 中最后提到的组件数据树的这个方案,这是最底层的,用户用逻辑代码来操作数据源本身可以解决任何问题,这在简单的场景中已经足够用了。但是在一些更复杂的场景、例如“先要查询一下表单中的每一项是否都已验证,再决定发不发请求”等场景时,让用户一个一个读组件数据很不方便,那我们就在这个基础上对用户的逻辑代码中注入一个 form 的对象,这个对象上的方法能帮他快速读取表单组件数据。但本质上,还是基于原本的组件数据树。这样对用户来说无论哪种场景,无论代码怎么写,都是兼容的。学习是一点一点递进的。
同样,易扩展指的也是和渐进一样的场景,只不过描述的对象是开发者。有了上述的这个统一操作数据树的方案,我们就可以不断的增加新的辅助对象(称为util),来处理不同的场景。例如发送 ajax 时组件自动呈现 loading 状态等。有了易扩展的特性,才能是系统以最小的成本去适应不同的场景,同时也提供了让第三方开发者来为平台提供能力的可能。
最后的“程序可读”则是保障系统稳定、应用不失控的关键。我们的系统中一开始就定好了用户写的代码只是对“组件事件”的响应代码,并且如何操作组件数据树、如何使用util都是有严格规定的。有了这个基础,我们就可以对用户的逻辑里的意图有非常强了解,例如我们现在可以通过扫描代码来在运行前就直接提示,哪个组件会受到哪些逻辑的影响。组件改了名字,哪些逻辑代码也要改等等。同样的,通过 debug 状态下对 api 包装等手段,还能做到更强大的调试功能。这也是对“用户积累一定经验后,凭什么放弃灵活性,继续用你的系统”的回答,因为用户放弃的灵活性,我们可以为他提供更强的保驾护航。
最后,回到前面的话题,我非常同意 “灵活” 和 “高效” 是天平的两端。但其实可以不去纠结这个事情。之前有一本讲交互设计的书给了我很大的启发,其中提到要把用户设想成“聪明但是很忙的人”。“聪明”意味着你给他抽象如果比较常见或者简单,他是能够很快掌握的。忙意味着你要从他的角度思考,当掌握了这个抽象之后,怎么样能加速他的工作,或者能提供给他什么途径让他能自己加速自己的工作。只要达成了“让聪明人很快的干完了事”,其实就算成功了,用不了你产品的人可能就真的不适合用你的产品。从这个角度去想就会发现,以前纠结的“灵活和高效有矛盾”的原因是,高效里面有“聪明人的高效”和“蠢人的高效”。“蠢人的高效”意味着你什么都要封装好给他,这会和“灵活”相悖。所以放弃掉“蠢人的高效”,也就不用考虑这个问题了。
问:
从设计稿直接上线可用的代码,其实最难的是需要设计师来配合,因为这会给他们增加约束,如何来处理这个问题?
答:
相比于具体的技术问题,这个问题其实确才实是最难解决的。我在文章里其实稍微提到了点,约束是“自动化”的关键,是无法避免的。在 airbnb 的这篇文章 https://airbnb.design/building-a-visual-language/ 中也提到了这个。既然无法避免,那最关键的就是这种约束如何为设计师也带来好处。我们的文章里提到了对设计师的约束其实只有一个:“要以某种指定形式来规范图层,例如命名,这样才能将图层和要生成的组件关联起来”。我们现在已经启动了这个项目,设计师是直接参与进来的,和他们交流中总结出来的对他们有价值的几个关键点:
1. 流程上受益。如果平台能保证只要设计师按规范出图,那么最后上线的效果就一定和设计效果一样的话,那设计师就不用在最后上线前复查真实页面了。
2. 对设计资产的管理。“组件”这个概念其实是前端和设计师通用的,前端通常是以代码库的方式来管理组件,而设计师目前缺乏相应的工具或者规范。如果设计稿里能通过程序读出设计师使用了什么组件,如何使用的,那么我们就有机会为设计师去也去做一些设计资产管理的工具。例如设计的组件库管理工具,当整体设计风格升级时,能自动告诉设计师哪些页面上相应的组件也要升级,如果设计的搞好,甚至可以自动升级视觉稿。
3. 满足设计师的产品心。如果这个系统稳定性、易用性好到一定程度的话,设计师其实也有机会直接作出可以用的产品。这本身对设计师也是很大的诱惑。
除了上述这些具体的点,其实最重要的是要让设计师直接参与进来编程系统的设计者,这样自然就破除了职能之间本身存在的隔阂。
问:
为什么会想到做这样的东西,另外什么时候开源?
答:
这几年无论是做框架还是工具,我一直是朝着提升 web 应用的研发效率这个方向前进的。在见证了前端新的基础技术(浏览器环境、语言)和框架的不断变革后我意识一点:
“对个人来说其实不存在研发效能的问题”。因为一个人能处理的问题有限,同样要用到的东西也是有限的,工具不好用换一个就是。编程理念不对学点新东西就是。但是对于大公司来说,问题就发生了质变。首先,资源的不对等会产生“短板效应”,例如前端人不够,不可能因为这个拖累整个产品的研发吧?所以有的部门加班,有的闲。再例如职能划分的过细、流程太长导致中间的沟通成本急剧增加,也会极大拖累整体效能。
之前我一直在框架、工具中探索,“找到某种正确的抽象”、“实践某种编程理念”,这其实都是针对个人的方案。而业界所看到针对团队的方案普遍也就是建立“组件库”、建立各种“发布平台”、“监控平台”等等。这些都是从提升“某个工具的复用能力”去解决的。而比较少看到专门针对流程的方案。见证了那么多框架、工具的兴起和死亡之后。我认为从“流程自动化”的方向去解决问题才更能帮助大团队和大公司解决问题。因此这篇文章里的其实最重要的并不是具体的实现技术,而是 “设计-研发-测试-部署” 的全流程串联,并且在流程上实现自动化。关于这一点我会再单独写一篇文章,我们在 web 应用研发这个方向应该已经慢慢进入新的阶段。也欢迎有兴趣的读者来信和我交流。我会持续更新到这篇文章中。
平台会逐步以工具的形式开源,如果希望参与也可以先联系我。