查看原文
其他

万字长文:从产品设计到发布的全链路智能化

钱思成 前端之巅 2022-11-10

本文由 InfoQ 整理自百度资深研发工程师钱思成在 GMTC 全球大前端技术大会(深圳站)2021 的演讲《从产品设计到发布的全链路智能化方案》。  

作者|钱思成
编辑贾亚宁

非常荣幸受邀参加今年深圳站的 GMTC,现场四十五分钟能介绍的内容有限,通过线下交流感知到大家对这个议题较为关注,所以通过本文来补充信息、抛砖引玉。

全链路诞生的背景

我所在的团队主要负责搜索和分发的内容呈现,产品迭代较频繁,年均需求 1500+。模块涉及前端研发计约有 150 人,上下游合作的产品、设计、研发、测试各约 200 人。

公司内部是按照角色划分团队管理的,所以实现一个业务,是由各个团队出人来协同。研发永远稀缺,伴随着业务高速地扩张和迭代,形成了各业务竞争研发人力的局面。另一方面因为业务和组织结构经常变化,间接导致各职能和平行部门协作成本高、效率低,人均有效工作时间不足 20%,产品交付周期长。

图 1-1 某业务交付现状案例

从 2020 年开始,研发侧增加了效能提升的技术方向,希望通过工程优化缩短迭代周期,消除人力缺口;而此时设计团队也计划从 0 到 1 为 BG 实现一套设计系统,所以双方一起合作产生了无极(Cosmic)平台 1.0。

1.0 版本在功能上主要实现了基于设计系统对样式管控,以及可视化拼接模板机制。上线后,从结果看释放了一部分研发人力,但又需要新的人力去维护这个平台和研发平台组件,前端团队的人力缺口问题并没有因为平台上线就得到了显著的改善;对设计、产品运营而言有部分效率的提升,但仍有很大的优化空间。

所以从 2021 年初,研发和设计继续合作研究业界动向和创新实践,试图找寻一个全链路提效的方案。

目标、路径与手段

梳理产品生命周期的主要流程和每个阶段的工作模式,可以得出下图:

图 2-1 产品生命周期现状

在产品、交互、设计、视觉代码实现这些阶段存在 2 个比较显著的问题:首先每个阶段工作的产物没有托管,自己复用、团队内复用或跨团队复用率都很低;其次每个阶段的交付产物对于下个阶段的复用价值也很低,基本没有通过转换实现的可能,仍然需要人力来实现。

上述问题在很多团队存在,产品和设计团队一般很少引入协同工具和技术到工作中,所以产品和设计需要保持大量人力投入。同时因为不能收敛需求和设计,也会造成高额的研发成本。

所以理想目标就是全链路上的信息是可以转换和高效传递的,同时所有工作产物都尽可能复用:

图 2-2 目标产品生命周期

这就引出了两个关键的优化路径:提升协作效率、增强协同能力

提升协作效率的理想态就是专人专事。以视觉为例,设计师对其负责,完成设计(也就是最终线上页面样式的呈现);视觉对研发透明,这样就能省略交付和走查验证的流程。

图 2-3 视觉与研发解耦

类似地,其他角色也尽量向工作链路提供有效信息,并且对应到最终实现。

图 2-4 多角色协同优化

要做到这一点,核心的手段就是使得信息的收录、存储、传递标准化,并落实到工具。在设计标准时,需要考虑信息的分类与内容、信息收集对象和收集成本。

图 2-5 某 I2C 方案分析

这里以一个 Image to Code 方案为例:虽然研发可以少写或不写代码,但是这类转换工具要求设计师的设计稿遵循一定规范。这些规范有的是约束图层的结构层次,有的是在图层上标注信息。这些做法导致研发成本转嫁到设计师身上,同时增加了设计师的心智负担。

在后面的内容里我们会展开介绍,如何通过信息的标准化建设提升信息收集效率。

针对增强协同能力路径的主要手段是增强对资产的管理和复用:

图 2-6 增强协同能力

基于标准生产的优质资产能够帮助内外部团队在实现同质化产品时能够直接复用或快速参考,从而消除因为零启动造成的人力消耗。

全链路方案的演进

产品迭代的整个过程中,不同角色在一个链路上不断完善信息。开始大家提出一些模糊的想法、创意,进而转化为领域范围内比较确定的描述,然后通过代码的形式精确承载,最终以可运行代码的形式发布。

首先,某个阶段已录入信息的不全面、不确定都会导致这个链路无法继续进行或反复,因此这是标准化首先要解决的问题。

其次,还存在不同工具和平台之间信息载体的差异,造成信息不等价问题。最明显的体现就是设计工具和平台(iOS / 安卓 / Web)布局很难通过规则转换来完美实现,本质上因为这些布局能力不尽相同。不谈实现成本,即便在设计工具中提供 Flex 布局能力,可能也会招致设计师的抵制:一方面有些约束原先未被录入,且以默认存在的方式让研发心领神会地实现,完全的布局致使工作量陡增;另一方面 Flex 布局的实现过于碎片化,对逻辑和空间感的要求超出了设计师的能力。因此,制定标准的时候还需要结合现阶段角色的行为、平台和工具进行平衡,甚至需要各种过渡方案。

所以对于全链路的优化,我们提出了 3.0 的远景版本和一个更现实可落地的 2.0 版本。

3.0 版本指的是一个理想的方案,在一个平台或引擎中,不同角色按照不同的工序和流程,为最终实现逐步添加信息直至发布。这样的协作模式在 3D 或游戏领域更为常见,如 Unity 或 Blender,因为 3D 行业的资产交付成本太高,不得已会催生出各种标准化的一站式引擎平台。

在这样的一个引擎中,解决上述布局问题的空间将变得更大,除了打造我们想要的布局系统,也可以将布局的过程分为静态、仿真、完全实现几个工序来交给不同的角色,使信息生产的成本曲线更加平滑。

脱离引擎,强行制作插件在现有的工具中标记信息和传递可能造成高昂的学习、使用、维护成本,所以一些功能(如布局、交互、动画等)考虑在 3.0 去实现。

2.0 版本相比 3.0 版本,更关注落地实施的可能性,优先考虑社区工具和标准,来满足低成本的信息输入要求,避免拉长战线导致业务接入困难。同时核心要实现样式、结构、逻辑的解耦、组合、复用机制,来满足样式的管控诉求和业务组件的多样性。

  • 样式 层面主要依托 设计系统 实现视觉对最终样式的管控能力

  • 结构 层面主要依托 DSL 来维系不同生产阶段对需求的一致表述

  • 逻辑 部分主要在编码阶段实现,主要关注点是在组件化方案中逻辑和样式、结构如何保持解耦,以及逻辑本身的可复性

谈论到设计系统,很多研发不太了解,所以我们来明确一些概念:

  • 设计系统 Design System 是为了实现数字产品的方法论,目的是视觉统一和风格明确,包含设计语言以及相应的实践方法。

  • 设计语言 Design Language 指的是解构页面元素,以复用性为目的抽取的可感知元素(颜色、大小)、功能元素(间距、栅格等)和规范。设计师使用设计语言进行设计,以往设计语言的表现形式可能是一张贴在设计师工位的纸质说明。

  • 实践指如何创建、捕获、共享和使用设计语言,通常如执行标准、实施方案,使设计系统运转。

  • 设计令牌 Design Token 是一种执行标准,将设计语言代码化,统一提供给设计师、开发者,形如 $button-primary-color = #00FF00。

  • 设计系统管理平台 DSM 指以平台化的方式实现对设计语言的管理。

  • 资产通常包含一切可复用的电子化的工作产物,如设计令牌、设计稿、设计组件、代码组件。

Cosmic 2.0 通过设计系统对设计语言进行管控,向设计师和研发提供一致的 Design Tokens,设计师的工作产物在资产平台托管,并通过版本管理形式发布到代码资产平台,研发基于 Tokens 和设计师工作产物进行代码组件的实现并上传,接着基于可视化生产工具进行组件拼接模板,完成后交付给下游环节参与生产交付。

图 3-1 Cosmic 2.0 架构图

2.0 大部分内容,其实与设计工程化和组件化相关,接下来我们会分别展开介绍。

设计系统

这个章节我们主要会围绕 Design Token 去讨论如何实现基于设计系统的样式管控机制。

Design Token 其实是近两年前端比较关注的标准,社区现阶段的草案和信息你可以在 https://design-tokens.github.io/community-group/ 获取。

 实践困境和思考

Design Token 语义描述本身可以简单也可以复杂,如 blue-400、color-primary、cosmic-color-primary、cosmic-dark-button-text-size-sm ,通常你可以自由定义 Token。

但和大部分实践了 Design Token 的团队沟通,反馈都是使用过程中会发生很多问题,比如设计师不理解如何选择和使用 Token 或者 Token 使用成本高。

通过分析 Token 语义构成的复杂程度和使用成本可以解释这些问题:

图 4-1 Design Token 使用成本分析

Token 语义如果足够简单,诸如 blue-400 ,还比较符合设计师的直觉,原来他选择颜色,现在使用 blue-400 的 Token,起到了快速定位的作用。但是如果一旦提供了 bg-active、button-active 甚至 news-button-active 这样的 Token,设计师可能就会陷入迷惑:“什么是 active、什么是 button?我能否在 hover 时使用 active?”毫无疑问,语义的复杂增加了设计师的使用成本和心智负担。而对于设计师而言,提供的 Token 越简单,越符合设计时的直觉思维。

而研发面临的情况却恰恰相反,如果一个设计系统向研发只提供原子性 Token ,那么研发会基于复用的角度进行聚合,聚合的结果不论是高阶 Token 或者是 Class,他们的名字可能都跟 bg-active、button-active、news-button-active 相接近。所以提供给研发一个信息量大的 Token 直接关联到节点更为方便。

Token 语义复杂度和使用成本的平衡是客观存在的,其实我们需要向设计师和研发提供不同的 Token,并且对这两种 Token 及中间态进行管控。因此我们首先需要对 Token 语义进行思考和基于业务的抽象。

以下分类思路源自 Nathan Curtis - Naming Tokens in Design Systems ,可参考了解概念,但根据全链路标准和实践有部分改进。

我们可以将 Token 语义分为四大类和若干小类:

图 4-2 Design Token 语义分类
  • Namespace 命名空间,通常一个设计 / 开发 / 生产环境是基于命名空间的,命名空间的切换相对于环境是全局性的

    • System 系统或框架前缀

    • Theme 主题名称

    • Mode 模式(暗黑 / 日间 / 夜间),基于我们的产品不会单独实现局部的 mode 变体,而只会进行全局性 mode 切换,mode 放入命名空间类别中

    • Domain 逆序 host,通常用以区分子业务,如 com.baidu.search

  • Entity 实体,实际存在的东西

    • Group component group 或 页面 、模板

    • Component 组件

    • Element 组件内部的关键节点、嵌套组件

  • Variant 变体,用于修饰实体,采用 Variant 而非 Modifier 考虑到和设计工具概念(Figma)保持统一
    • Use Case 用例场景,可以有多组,如 primary / secondary 、base / warning / error

    • Interactive State 交互状态,如 hover / selected / disabled

  • Base 基础部分,一般和属性、值相关
    • Category 属性大类,一般有 Color、Text、Space、Border、Size、Shadow 等,和设计工具规范分类关联

    • Concept 抽象概念,用以分组,如:feedback、headline、title。注意不要出现实体和变体内容

    • Property 属性名称,如 Border 的 radius

    • Scale 值分档,如 xs / sm / base / lg / xl 、50 / 100 ~ 900 等;需要对 Token 按值分阶细化时使用;Scale 可能与 Variant Case 语义接近,比如组件可能有大中小场景,但代表的不是一件事情

Cosmic Deisgn Token

Cosmic 2.0 将 Deisgn Token 按照复杂度不同分为四类:

图 4-3 Cosmic Design Token 分类
  • Global Tokens 全局令牌,或狭义上的设计变量;语义只包含 Property、Scale 部分

  • Alias Tokens 别名令牌,aka 设计师令牌;语义只包含 Category、Concept、Scale 部分,按照设计师工具所支持的 Design Library 格式进行聚合。Alias Tokens 是设计师在工具中可以直接使用的 Token 形式

  • Component Tokens 组件令牌,在具体场景(某组件某变体)关联使用何种 Alias Tokens

  • Production Tokens 生产令牌,在具体环境(指定命名空间)关联生效何种 Component Tokens

Global Tokens、Alias Tokens 是预先定义的,是研发或设计师会接触到的 Tokens;而 Component Tokens、Production Tokens 并不会预先定义,是随着生产过程逐步产生的,后两者存在的价值用于样式管控

图 4-4 不同 Token 产生阶段

如果一个团队从 0 基础开始打造设计系统,第一步要进行 Global Tokens 的定义。按照上述概念,Global Tokens 语义包括属性和值分档。属性其实并不需要深度研究,基本雷同,而值分档和 Token 对应的值是定义 Global Tokens 的关键。一般做法是 Global Tokens 提供一个大而全,且有规律性的集合,以便于能够覆盖现存业务或未来业务可能使用的值(或接近映射)。比如 Color ,可以按照 PCCS 色彩体系选取色轮,再按照 50、100~900 方式进行分档(颜色具体的算法非常复杂,后面有机会单独介绍 Cosmic Color Token)。对于通用业务和大型团队,Global Tokens 需要足够包容,也许色盘很大、值很多,但一定程度已经定调了 Tokens 使用成本的上限。

Global Tokens 定义和业务相关性不大,但决定了设计系统管控未来接入业务的能力。而 Alias Tokens 的定义,其实更接近于 Design Language 的构建,因为 Alias Tokens 一定程度上已经决定了产品视觉的调性。也正因为如此,Alias Tokens 一般会在不同业务线有所不同。

Component Tokens 如上述,是需要实体、变体信息的。显然,设计师无法独立提供此类信息,所以在正式设计之前,需要有一个组件库提案的流程,一般是产设研共同参与,产出一份需求描述。我们要求这份需求描述最终以电子化的形式承载,即一个简约的 DSL,需要包含组件名称(Component)、包含的关键节点(Element)、组件变体(Variant)信息(Case、State)。

这份 DSL 非常重要,它作为标准后续将维系设计与研发进行一致实现。并且我们将其托管在资产平台,任何人或工具可以通过一个唯一链接来获取,形如 https://HOSTNAME/c/button@0.0.1

图 4-5 Token 使用与设计工具结合
  • Step 1:设计师在 Figma 中创建组件,并且在 url 属性字段中填入托管 DSL 的链接

  • Step 2:根据 DSL 中的关键结构创建图层,进行设计(使用 Alias Tokens)

  • Step 3:根据 DSL 新建组件变体信息

  • Step 4:选择某种变体场景,修改该场景下的外观(改变 Alias Tokens)

上述步骤均基于 Figma 标准能力,而 1 ~ 3 步可以考虑依托 Figma 插件从平台获取 DSL 并初始化组件、关键结构及变体来提升效率。

组件设计完成后,同步到资产管理平台,就可以提取出 Component Tokens。

图 4-6 Component Tokens 管理和泛化

通常,设计师不一定习惯在设计工具中实现所有的变体组合,他可以通过在平台管理 Component Tokens,比如泛化出更多组件不同命名空间、场景下的 Component Tokens。上图所示就泛化出了 dark / light mode、primary、disbaled 时相关的 Component Tokens。

图 4-7 DSM 的输入和输出

综上,我们的设计系统的输入有基础语义的定义、DSL(实体、变体语义)、设计稿。给设计师侧的输出是可用的设计工具规范 Library (Alias Tokens);给研发侧的输出是 Global Tokens 、Alias Tokens、Component Tokens 及一些变体信息,形式可以是 CSS、SCSS、LESS,也可以是原子化 CSS 工具的 Config 文件,如:

/** rollup.config **/import { globalCSS, // 用于 scss 预编译的输入} from "https://HOSTNAME/c/@baidu/meg/scss@lastest";

而组件代码实现时,其实是对 DSL 结构进行完善,比如需要创建关键节点 icon、.text ,使用变体切换样式:

/** Button.svelte **/<script>import type { InteractiveState, GeneralSize } from "https://HOSTNAME/c/@baidu/meg/button@lastest";export let state:InteractiveState; // 实现组件状态 hover / disabled / selected / ..export let size:GeneralSize; // 实现组件大小用例变体 xs / sm / base / lg / xl /** 交互实现 **/</script><!-- 双向绑定变体变量 state、size --><section class="{ state } { size } text-base border-xs"> <icon …></icon> <span class="text"> { text } </span></ section >

可以看到上述的代码基本做到了结构、逻辑、样式的相对分离,理论上基于 DSL ,是可以生成一份初始化代码的。

对于组件编码实现有个重要的规范约束:不要使用动态拼接的 ClassName 进行插值,而使用 Token、VariantType 或 自定义 ClassName。

从编码角度,在代码中拼接 ClassName,会造成逻辑与样式的耦合。使用样式(Token)、结构(Element)、逻辑(Variant)来组织代码本身就是一种解除耦合的设计模式,后续我们会提到基于这种模式如何实现各种样式配置化能力。

从编译角度来说,动态性的 ClassName 对于 CSS 预处理很不友好,对 PrueCSS 或其他优化机制实现造成阻碍。

最后谈谈编译相关。实质上,设计师的工作提供了 Component Tokens,等价于 CSS 样式;而研发通过实现结构,使用变体,创造了 CSS Selector 相匹配的元素。在 CSS 编译阶段,会分析组件的关键节点(源自 DSL),结合实际使用到的变体类型、自定义变体类型,生成 selector,并且与 Global CSS 进行匹配,通过 PureCSS 操作精简出最终有效的 CSS。

图 4-8 Design Token 参与样式构建
和社区实现的对比

大部分的社区组件库提供固定的 Tokens 用于样式管理,但 Cosmic Design System 还提供 Tokens 的定义和语义管理,主要是为了满足产品和设计新增和持续迭代业务组件。

避免设计师使用复杂 Token 的同时,也降低了 Token 的选择成本。

图 4-9 业界组件使用 Token 比较

同时,在编码侧,对 Token 的使用也有完全不同的表现。如上图,社区组件实现一般会在 JS 中进行 CSS 拼接,来实现变体有关的功能,导致代码可读性和可维护性下降。同时在 CSS 中直接使用  Component Tokens ,实质上将结构和逻辑信息体现在了 CSS 中。CSS 本身就是用于样式和结构分离,直接使用  Component Tokens 参与编码的方式明显违背了这一初衷。

重新认识 Design Token

通过以上介绍,可能你已经感知到,向 Design Token 赋予复杂的命名,试图融合结构、逻辑、样式的信息,本身就违背了 SoC (Separation of concerns) 的原则,我们应当在实践中避免直接使用带有结构和逻辑的 Token。而在上述的方案中 Token 与结构、逻辑的关系,我们用需求 DSL 去承载,并且由 DSM 托管他们之间的关系(Component Tokens)。

关于这份组件需求描述 DSL ,我后续会深入地进行介绍;这次 GMTC 深圳 2021 也有一场概念相近的演讲 侯振宇 - 需求结构化表达为研发效能提升带来的新展望,他主要是从更大的粒度来分析需求如何结构化表述,大家可以关注一下。

此外,有了对 Design Token 的认知,我们也可以分析下 WindiCSS 和 TailwindCSS。在这两个原子化 CSS 工具的实践中,我们也遇到了各种各样的问题,因为工具提供的原子样式粒度上与 Global Token 无异,编码时难免要根据结构或逻辑来聚合样式达到复用的目的。在稍微大型的项目中,因为缺乏类似 Component Tokens 这样系统性的样式类,是无法做到开箱即用,除非你放弃对样式的管控和可维护性。

图 4-10 原子化 CSS 工具

所以基于 WindiCSS 和 TailwindCSS 的最佳实践需要和设计系统结合,补充抽象信息(如结构、变体)以提供更多易用的样式类。

组件化方案

通过 DSL  + Token 的机制,已经能够实现设计对样式的直接管控,上文中也介绍了部分组件方案中编码和编译相关的工作,本节会继续介绍一些其他的组件化相关方案,思路依然是保持样式、逻辑、结构解耦,来降低视觉和功能的复用成本。

图 5-1 设计师管控样式
样式模块化

上述的样式管控机制配合 DSM 按照命名空间对 Tokens 进行管理,就可以控制不同生产环境生效的样式,以此实现不同命名空间下组件拥有不同的外观。

图 5-2 业务组件样式泛化

但是这种外观控制范围是基于命名空间粒度的,有时候同一个业务,或者同一个页面,对于不同组件有不同的外观需求。并且可能这种外观需求并没有通过用例变体内置在基础组件中。为了解决这种业务需求,我们会用组件预制(Prefab)来满足。

预制可以理解为组件外观不同的分身,我们希望设计师在工具中使用预制、研发在编码时使用预制的体验和组件是一致的。

在设计工具里,组件的实例用到的 Token 可以进行替换,以 Figma 为例:

图 5-3 Figma 中通过 Token 替换自定义组件实例外观

设计师要自定义 Card 组件实例的外观,通过 Selections Colors 面板进行颜色 Token 替换。

通过这种方式自定义设计组件实例的外观非常方便,但传统代码组件并无样式接口支持这种动态外观设置。Cosmic 2.0 中,这种自定义外观组件会转换成组件预制:

图 5-4 外观自定义组件实例关联预制文件

设计资产同步到资产管理平台后,外观自定义的组件实例会转换成预制文件,如 new-button.prefab 。

在编码阶段,研发使用方式如下:

import Button from './button.svelte';import NewButton from './new-button.prefab';

这个 new-button.prefab 的预制文件内容如下:

{ "name": "NewButton”, "import": "./Button.svelte", "tokens": { "light": { "bg": { "value": "blue-400", "extensions": {"variant": {"active": … } } } , "text": { "value": "head-1", "extensions": { … } } , "icon": { … } , }, … }}

  • name 即 prefab 的唯一标识

  • import 声明是基于哪个(版本)组件进行外观配置

  • tokens 是用于改写外观的 Component Tokens,格式与 Design Token Modules 标准一致

图 5-5 Prefab Render Tree

import 指向的组件是基于 DSL 实现的,从中可以提取到结构和信息,综合 Prefab 文件提供的外观 Tokens 可以产生可视化编辑用途的 Schema 和 Render Tree。

在设计和研发阶段, Schema 与 Render Tree 可用于 Prefab 的可视化编辑器来展现编辑界面和默认值;也可以帮助 VSCode 扩展来进行 Token 编辑时的代码补全。用户在可视化编辑器中可以重新选择 Tokens,进而产生新的 Prefab 文件。

图 5-6 Prefab 配置界面生成和配置过程

在构建阶段,Prefab 预制文件实质上提供了 CSS,并且能作用于原组件,通过编译生成新的组件类,并且与原组件 Scope 不同,来保持样式隔离。

图 5-7 Prefab 样式编译生效流程

最终我们在一个页面引入一个组件对应的不同 Prefab,可以获得多种外观表现。

同时也需要关注到,在引入预制概念后,组件研发团队的成员初期会有一些疑惑,是不是所有的外观变化都可以通过预制来实现?接口里形如 size 这样的变体是不是也可以通过预制来实现?组件是不是可以取消所有外观相关的属性?

经过实践和讨论,我们给出了一些判断标准和原则:组件内聚了一套外观,这套外观内的切换是基于组件状态(变体)的,一个组件实例的状态是会切换的;而多个预制代表了组件多套外观,通常组件实例化时是明确使用一套外观的,并不会因交互而改变预制。状态(变体)对组件外观的影响具备一定逻辑性、结构性,这些与设计语言的实现相关联因而种类可以收敛;而预制对组件外观的影响较为随意,更多是功能和需求的实现,并且预制数量也会随着业务复杂度增长而增长。

图 5-8 Native UI 的 Button 组件

分析上图中的 Button 组件,Type 分类在实际业务场景中都会经常使用,并且存在切换可能性,所以需要以变体方式实现;而基础 / 次要按钮 / 次次要按钮 / 次次次要按钮这样的概念则可以从接口中取消,以预制承载,理由是基础组件库提供这样的分类,业务不一定会使用,即便使用了也不一定能正好满足各种复杂的业务线,并且一个组件实例通常不会在这几种外观之间切换。

以 Cosmic Button 组件为例,看看有了预制的概念后,其接口和实现有什么变化。

首先设计和研发,前期经过讨论产出 Proposal,定义了关键节点、代码组件的属性和设计组件的变体:

图 5-9 组件 DSL 的定义

可以看到,核心变体只有 type、size、state,代码组件接口也很简约:

  • type 代表了用例场景不同视觉会有差异,并且这些用例场景是业务常见的,所以内聚到组件中,通常选择不同颜色、边框样式进行组合;

  • 和 type 不同,size 代表了组件大部分有值分档的属性,如 spacing、width、height、marigin、padding 的默认取值,是统一尺寸的表现。size 内置在组件接口,实质上是将关于尺寸和布局的设计语言内聚其中;

图 5-10 Button 的 size 属性
  • state 代表交互状态,一般研发只设置 disabled,但设计侧需要提供所有可选择的种类。

设计工具中,设计师拖入 Button 到画布中,选择了 type、size、state 后,可以通过 Token 替换方式进行原子性的修改,这种修改都将以预制形式承载;对于研发侧的实现有两种选择,设计师将其沉淀为一个预制组件,供研发复用样式,或直接使用 CSS 改写样式实现。可能一些研发在实践过程中会发现取消自定义样式接口改用样式实现会增高成本,对此我们的态度是:本质原因是设计偏离了规范,过度使用 Token 替换,所以产生了这样的代价;如果这种偏离设计可以沉淀为预制,那么只有一次成本;如果每次都偏离,那就要反思设计和产品是否有遵从设计原则和是否正确实践了组件化设计。

对比业界组件实现,很多属性都归类到如下几种方式实现,而未出现在接口中:

  • 预制 /CSS 实现:bordered、circle、color、round ...理由:这类外观参数过于原子化;面对复杂组件,一个不包含结构前缀的属性不足以描述作用对象

  • 预制实现:fill、type = 次要、次次要 ...理由:组件实例不会动态切换这些类型;基础组件库无法抽象出来满足业务的分类

  • 插槽实现:icon-placement、loading ...

  • 布局类组件实现:block、grid ...

  • 超链组件实现:href、target

最终通过精简后,设计组件和代码组件关于视图部分的接口基本一致,并且预制机制也很好地对应了设计工具中自定义样式的功能。

组件通过这样的方式进行改进后,满足了业务组件的丰富度和沉淀要求,凡是外观不同的需求都可以通过预制来进行泛化,而不必通过提 PR 或者 Fork 的方式来实现。

图 5-11 业界组件库可配置化实现比较

同时预制的可配置化实现是基于独立模块,与基础组件解耦的。社区的可配置化组件代码往往可维护性和扩展性都很差,比如需要声明非常多的 Props 来传入样式(左图),不仅粒度划分考验设计能力,动态性的 CSS 注入也阻碍 CSS 预编译实现;或需要在 JS 里进行大量 CSS 运算(右图),可配置项和状态越多,运算越复杂。

预制的本质就是样式模块化。

逻辑模块化

逻辑模块化通俗说就是组件 JS 部分代码如何进行模块划分和管理,目的也是解耦和复用。

图 5-12 Cosmic 组件化方案分层架构

目前内部对于组件实现分为三个层,业务组件层和标准组件层的差异主要体现在外观,通过之前章节讨论的各种方案解决;而逻辑抽象层和标准组件层主要解决跨端、跨平台场景下实现基础组件时,通过继承和组合的方式来复用逻辑代码。

逻辑抽象层包含的模块基本是与视图、样式无关的实现,如模型、状态机、输入与输出、工具函数(算子),而标准组件层引用这些模块,并且会有与之相关的各种胶水代码。

我们也关注到近期开源的 semi 以及 material-components 使用了 Foundation / Adapter 机制来进行分层解耦。

相关内容:material-components-web、UI 组件库如何分层设计,使其具备适配多种 mvvm 框架能力

但是经过调研和讨论,目前实现没有选取使用类似机制,原因主要是:

  • 把多端的 F/A 代码相加,对比未使用 F/A 架构的实现并没有节约多少代码。首先 Adapter 层针对不同终端的 I/O 操作都要单独实现,而这块是大头,不论是写在 Adapater 还是组件里;其次真正要复用的 F 模块代码量较少,同时因为 F/A 架构增加了一部分代码,抵消 F 减少的,实际收益不大。

  • 从机制上看,调用过程比较绕,同时作为基础组件会加重整个组件库的运行负担,对于 ToC 和 SSR 场景而言, F/A 不一定适合。

  • 维护好 F/A 架构比较考验设计能力和编码水平,对大多数业务组件的研发来说有风险。

另外也考虑过最近比较流行的 Composition 模式,结合几种组件库的 composition-benchmark 结果来看,作为基础组件来使用也会带来额外性能开销,因此没有使用。

目前分层实现是基于简单的继承和模块引用。这里不详细罗列其中内容,但你可以通过一些类似的最佳实践进行了解,如 ui-model ,这部分内容也是我在 2017 年 QCon 汪志成(雪狼)的专题《ui-model,更纯粹的前端》开始关注的。

DSL + Token 的标准和规范是为了实现样式可配置化能力,而逻辑模块化同样也是为了实现交互、数据编排可视化配置能力。

图 5-13 逻辑结构化表达

当我们的 JS 是基于上述范式来完成逻辑的结构化表达,这段代码也可以通过低代码可视化工具进行维护,在后面 3.0 的节点编辑器小节中我们会进一步介绍。

平台实践
Cosmic 2.0

根据上述,2.0 从组件设计到整页的生产发布流程主要分为四个阶段,这四个阶段分别收集了不同的有效信息:

图 6-1 信息全链路演化过程

其中模板可视化编辑是在一个基于 H5 的可视化编辑器中实现的:

图 6-2 模板可视化编辑器

每个阶段的产物,如 DSL 描述、设计稿、组件代码、模板都在资产管理平台进行托管:

图 6-3 资产管理平台

同时资产平台也基于命名空间对基础组件、基础 Tokens 和业务组件、业务 Component Tokens 进行区分管理:

图 6-4 基于业务命名空间的资产管理架构

资产平台除了资源托管,也提供了各种流程管控能力,比如设计可以基于某个命名空间下的设计语言变更来发起一个流程:

图 6-5 样式管控流程
智能识图与转换

基于 2.0 的工作流程,是先在高保真设计工具(Figma)中进行组件设计,后在 H5 可视化编辑器中进行可视化编排。但有些团队因为客观因素,如设计工具使用 PS/Sketch,习惯于产出整页效果图。为了便于这些业务的接入,平台推出了一些智能工具来帮助业务实现分阶段接入。

图 6-6 智能识图转换模板

上图的示例,就是上传一个完整的图片设计稿(PNG 格式),通过 AI 能力分析用到的代码组件和配置项,快速生成可视化编辑器中的模板。

图 6-7 智能识图转换模板流程

智能识图与转换的实现原理主要是基于 AI 组件识别及 OpenCV 特征提取,主要经历以下阶段:

  • 数据处理:样本收集主要来源于两个路径,主要是将用户访问日志提取的 URL 作为 Puppeteer 输入获取渲染截图,根据组件 dom unique 标记和组件位置获取组件标记信息;如果线上数据较少,则通过平台已有的代码组件,结合 Mock 数据渲染获得截图,根据配置的组件区块信息和配置位置,生成组件标记信息。

  • 模型训练:获得样本(截图和组件标记信息)后,我们使用 Faster-RCNN 目标检测训练模型进行训练,训练是基于 PaddleDetection 开发套件;无极平台覆盖 20 个组件,每个约 1000 张样本,识别准确率为 95%。对于失败的 Case 可以使用样本增强的方式进行循环训练直至达到满意的结果。

  • 模型推测:将训练好的模型部署到平台服务器识图模块,用户上传图片后,调用识图服务进行推理,识别出图片中存在的组件信息。

  • 特征提取:如果设计稿中的组件,与默认样式有差别,还需要通过 OpenCV 去提取 UI 特征,通常是边距、背景色、圆角等信息。某些组件可能存在一些设计约束,需要对识别的值进行调整。

  • Token 映射:Open CV 识别的值通常是绝对值,还需要同设计系统提供的 Tokens 进行映射获取对应的 Global Tokens。结合基础组件的 DSL,生成 Component Tokens。

  • 生成模板:对于个性化的组件实例,综合 Component Tokens 和组件信息封装成 Prefab。Prefab 和 基础组件根据 AI 识别的模板结构化 DSL 进行拼接,最终生成整个页面的 DSL。

图 6-8 组件样式泛化

基于 Tokens、Prefab、智能识图和模板可视化编排等机制,能够大幅提升业务组件生产效率和外观丰富度。

Cosmic 3.0

依托 2.0 平台基本实现了专人专事和资产复用,但是仍然存在非常大的优化空间。

图 6-9 Cosmic 2.0 协作流程分析

在流程上,设计师使用高保真工具进行组件设计、研发在 VSCode 进行代码组件开发、产品 / 运营在 H5 可视化编辑器中基于代码组件进行模板拼接。我们之前反复提到的产设研在提案阶段生成的 DSL,实质上就是为了保证跨平台协作一致性而产生的一种约束,保证大家都受约束的情况下提供设计师研发初始化的脚手架工具、Lint 检查和相应的交付流程。

同时 2.0 阶段的设计结构被浪费掉,代码组件开发阶段才真正实现结构。(智能识图只能负责高阶组件 / 模板嵌套)

因此 3.0 的主要构思就是所有角色基于一个平台来进行工作,将 DSL 约束内置化,大家感知不到约束的存在以及转换过程。从产品、设计、研发体验上来说,组件在需求、原型、视觉、交互、动画、逻辑、数据绑定各阶段都可以随时提供预览。

图 6-10 Cosmic 3.0 架构图

Cosmic 3.0 的核心就是一站式设计与研发引擎,提供面向多种角色的工作台。

一站式设计与研发引擎

一站式设计与研发引擎的技术难点主要是高保真设计编辑器图形化功能的实现,我和国内从事类似产品的团队了解到的信息是,实现这样的图形化编辑器研发成本至少 > 10 人 / 年。虽然成本可能很高,3.0 仍然放弃了基于 Figma 或 Sketch 来进行插件化实现,是因为实践中遇了不少难以解决的性能问题,以及它们提供的接口能力有限。

针对前面提到的外观个性化定制场景,Figma 提供了 Variant Props 和 Selection Colors 面板实现信息收集功能。但是对标 Web 能力,组件还有一些属性影响外观,比如折叠、缩略、maxRows 等信息需要收集,并且实时影响编辑器中的外观,这种能力难以通过插件来实现;时间轴、节点编辑器等同理。因此我们有足够多的理由去进行一站式设计与研发引擎的探索,同时也期待业界有相关的开源项目可以合作。

目前引擎在预研中,功能上包含了设计系统、资产管理、图形化编辑器(H5/Skia)、模板可视化拼接、流程及权限管理等模块,未来也会加入节点编辑器来实现动画、交互和数据编排功能。

图 6-11 一站式设计与研发引擎功能(Demo)

引擎定位是一个开源性质的平台,所有的功能和可视界面都是模块化、插件化方式加载的,并且能够互相通信交互。引擎提供开发框架、应用基座和核心模块的通用实现(如画布),第三方可以打造自己的模块和 Workbench(工作台)。一些思路借鉴了 Blender/VSCode/Figma/ Sketch 的实现。

图 6-12 一站式设计与研发引擎架构
  • Cosmic Framework:接近于微应用基座,支持插件化方式开发功能模块,并通过事件、IPC 等方式彼此通信交互。

  • Plugin/Module:所有界面可视的元素都是一个可插拔的功能模块,其包含 packages.json 及 main.js ,与 Sketch、Figma 的开发体验类似,但对于每个模块,微应用基座都提供了完全的能力,以便于模块实现各种功能。如下图所示,初始界面就包含 3 个视图模块(资源面板、画布、属性面板),后续可以通过面板分裂功能实现多个画布模块实例,不同画布可以设置不同的 Namespace(如 Mode),同步观察外观变化。

图 6-13 界面模块化
  • Workbench:针对不同的运行平台或目标功能可以定制 Workbench ,每个 Workbench 都是一个编译入口,Workbench 的配置文件记录所需的功能模块。如 Cosmic 中台(WebIDE)的 Workbench 主要包含设计系统、资产管理、模板可视化生产等模块;OSX/Win 设计工具的 Workbench 主要包含设计相关模块;VSCode/Figma Workbench 主要包含 DSM 和预制的管理,编译产物是插件。

  • Deps:引擎基于跨端特性选择 Electron 作为核心框架;组件化框架选择了 Svelte,主要是考虑到运行时性能,以及在编译为工具插件或 Web Components 模块时,没有 Runtime 依赖更有优势;高保真工具的实现选择 Skia 进行图形处理。

这里抛砖引玉一下:如果在一个引擎中实现了所有视觉和交互,意味着除了生产阶段编译成传统的 Web 语言外,也多了一种选择,就是直接将运行环境和引擎内的 DSL 打包发布,不再使用 JS/HTML/CSS 来承载信息。这样一来也就绕开了代码转换、Virtual Dom 和多端渲染等路径,而实现的关键在于开发工具和运行环境的探索。鉴于引擎使用的 Skia 也是 Flutter、Chrome 底层的图形库,这一路径也许是可行的,特别是本次 GMTC 提及的 WebGPU 相关议题也展现了更高效的图形化渲染的可能性。

节点编辑器

图 6-14 逻辑结构化表达

逻辑结构化章节中,我们提及了节点编辑器。节点编辑器其实就是提供一个画布,然后对象、函数、模块以一个个节点面板形式呈现,拥有输入和输出端口,如果一个节点的某个输出端口类型和另外一个节点的输入端口类型一致,那么就可以连线来传递数据。

图 6-15 Blender 纹理节点编辑器示例

节点也分为多种类型,每个节点本质上都是可复用的资产:

  • 输入节点:常量、变量、数据源、事件、时间轴信息、物体信息 ...

  • 过程阶段:各种算子或处理函数 ...

  • 输出节点:组件、远端 API ...

节点编辑器在很多大型引擎和软件中都存在,可以胜任动画、交互、纹理等一切逻辑的表达。像开源的 3D 软件 Blender 在 2.9 版本后的逻辑和配置化实现都是基于节点(万物节点)。

图 6-16 Unity 节点编辑器驱动页面交互

图 6-17 Blender 节点编辑器根据物体空间信息转换 RGB 改变物体颜色

图 6-18 Blender 中使用三个节点实现物体多样外观逻辑

节点编辑器的优势非常显著,在降低信息收集成本的同时支持复杂逻辑的编排。目前 XD、Figma 实现了一些简单的原型流程,能力上还不足以描述大部分交互。未来代码平台如果希望对逻辑进行管控,那么最可行的方式就是结合节点编辑器来实现。

智能设计

无论 3.0 还是 2.0 暂时都没有规划元素粒度的稿件转代码方案,主要考虑到优化信息收集路径比转换路径成本低,以及后者存在效果的不确定性和适用场景的限制。

图 7-1 嵌套布局

以布局为例,大多数团队并没有约定严格的布局信息(满足所有终端设备的效果)应当是由设计还是研发实现。而设计和研发依靠某种默契和经验来完成协作,设计产出上图结构,自认为表达了内部元素自适应外部容器的信息,而研发也能心领神会,这种默契一定程度上降低了双方信息录入的成本。

所以近年来智能转换方案的出现就是为了解决此类问题,但因为样本限制和准确度的要求,这些方案还是会最低限度地要求设计师做一些标注。做转换方案增加设计侧标注成本,不做转换方案增加研发侧实现成本。双方都不增加成本,只依靠有限的信息又无法满足智能转换后的效果,那么还有没有其他的路径呢?

智能设计可能是今后 1~2 年非常有潜力的方向,能够另辟蹊径降低设计与研发成本

智能转换是基于一个确定的特征图推测出结构和样式,还原设计的信息;而智能设计则是基于一定的构思、想法和约束,创造出设计。

目前业界已经有一些基于机器学习的智能设计实现,如 Neural Design Network 的模型接受关键结构和设计约束,推测产物也就是各种编排结果。

图 7-2 智能设计

以组件为例,关键结构信息即 DSL 描述的有哪些元素及基本实现;设计约束则是对这些元素提出的一些要求,如 title 元素必须在最顶部,图文是左右还是上下关系等等。设计约束相比精确的布局描述更为宽松,甚至可以由设计系统统一提供。

智能设计的模型可以由公共数据集或业务沉淀的组件进行训练优化,对于输出的结果可以有一系列评价标准来进行评判。诸如距离标准、美学标准、可用性标准等。

图 7-3 智能设计过程、模型、数据集及评价标准

关于智能设计更详细的原理、训练模型、评价标准及算法,你可以通过《智能设计之布局详解》 获得更多信息,并开始实践。

综上,设计师提供了关键构思、基于设计系统的约束和美术风格,模型就可以推测出一个最优解,体现在布局、配色和可用性各方面;同时基于 AI 的设计输出,也包含高密度的结构、布局信息,保障了代码的实现效果。

为了适配不同场景,设计师还可以通过调整设计约束,来获取不同的结果,如大字版、小字版。也可以将用户的画像及点击行为作为评价标准训练模型,来打造千人千面的设计。

智能转换的难点其实是需要依靠 AI 理解以往作品来推测现有设计的结构,而基于固定设计系统的设计样本不会很多,且需要推测的稿件可能存在主观违背设计系统的情况。而智能设计主要依赖设计系统、量化评价和数据集,创作过程没有人为的干扰,当然最终落地也要参考设计团队对结果的满意度。

智能设计能够汲取人的创意和想法,并替代视觉实现重复性工作,提供更多的标准化样稿和预览以供产品和设计决策,最大限度地发挥人与 AI 的价值。

总结和展望

全链路的智能化作为百度搜索和 MEG 前端团队的三年规划还在持续探索和实践落地,2022 年第一季度起会逐步开源一些标准和配套设施,欢迎有兴趣的个人和团队一起交流和共享实践成果。

图 8-1 Cosmic 实践效果和预期量化

从标准化、自动化到人工智能深度应用是一个循序渐进的过程,自动化流程离不开标准和模块化建设,而人工智能又深度依赖优质业务资产的沉淀。

2019 年之前大前端主要关注点都集中在研发的工程化;2020 ~ 2021 年逐渐有了新设计工具、可视化低代码平台或各种设计协同解决方案,本质上都是在进行设计工程化的探索,随着社区标准和工具的逐步完善,可行的解决方案会逐渐沉淀,被设计所接纳,虽然这一过程可能还将持续很久。

图 8-2 全链路智能化发展路径

而随着设计工程化在一些团队的落实,可以预测在 2022~2023 年会涌现出越来越多的自动化、人工智能结合的解决方案。因此,你也许有足够的理由从现在开始关注和思考,自己的团队如何去实践全链路或局部链路的优化。

图 8-3 全链路智能化展望

也许未来我们的产品经理、设计只需要和系统进行人机对话,描述产品需求和设计风格,然后 AI 就可以提取有效信息、调取相关的资产和视觉元素生成优质设计稿,并通过自动化流程,快速部署发布应用。

如果我们的技术进展到这一步,产品将有更多的时间打磨产品的创意和细节;设计专注于视觉与体验;前端也能脱离“切图仔”的定位,成为这样一个智能化系统的架构者和维护者。

最终,伴随着 AI 解放生产力,大家可以远离内卷,拥抱技术和生活。

鸣谢:errorrik、胖总、莉姐、孟雷、姚昌、柳叶、静媛、宇航、营帅、亚宁、宏彬、阳宏

参考:

Nathan Curtis , Naming Tokens in Design Systems

Lee H Y , Yang W , Jiang L , et al. Neural Design Network: Graphic Layout Generation with Constraints[J]. 2019.

Computer Vision–ECCV 2020: 16th European Conference, Glasgow, UK, August 23–28, 2020, Proceedings, Part III 16. Springer International Publishing, 2020.欣余 , 智能设计布局详解

如果你对全链路智能化有兴趣,欢迎加入我们或合作共建,联系方式:zhangli01@baidu.com

作者简介

钱思成,2012年加入百度,曾先后负责图片搜索、网页搜索性能优化和前端架构方向。担任极速搜索、简单搜索、手机百度App搜索的核心研发,长期推动搜索前端架构改善,主导了性能评价和监控体系建设、Node化、微前端化等项目。2019年起专注于研究产设研全链路协同的解决方案,旨在提升产品整体的孵化及迭代效率、设计和研发体验,先后参与无极产品设计中台、智能设计工具、D2R、智能组件库建设。

下周三(12月29日)晚20:00,小盖会连麦操作系统大拿魏永明,和他聊聊操作系统那些事情。这也是视频号本年度最后一场直播了,直播间准备了超多福利!感兴趣的话记得预约

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存