大家好,我是来自蚂蚁金服体验技术部的前端工程师江木,很荣幸能在 SEE Conf 跟大家分享:“云凤蝶可视化搭建的推导与实现”。
首先简单自我介绍一下,我在社区的 id 一般是 paranoidjk,我曾经参与过的一些项目有:移动端开源组件库 antd-mobile,蚂蚁的移动营销活动平台凤蝶,现在主要 focus 在企业级应用制作平台云凤蝶这个产品上。
我今天的分享主要分为三个部分,首先大概会花5分钟的时间介绍一下云凤蝶这个产品,包括它是什么,以及我们为什么做这件事情。第二部分会重点分享一下云凤蝶的核心技术与实现原理。最后会结合一些我们现在的用户案例来分享一下云凤蝶的产品现状,以及我们对未来是怎么看的。
首先第一部分是云凤蝶产品介绍。
不知道大家是不是都已经在上午的暖场视频都里看过下面这个云凤蝶的介绍短片了,我们可以通过这段短片简单了解一下云凤蝶。
相信看完视频的同学至少记住了一句关键的话,就是云凤蝶它是一个 “面向中后台产品的快速研发平台”,它的用户目前主要是是针对蚂蚁工程师,使用场景目前是专注在标准化的中后台产品研发,目标当然是为了提高效率。
那么为什么在蚂蚁我们会尝试做这么一件事情呢。首先是因为在内部的业务支持和数据统计中我们发现中后台业务越来越多,同时得益于 Ant Design 的广泛普及,这些页面在交互和视觉上也越来越标准化。
而伴随着业务需求的爆发式增长,前端技术本身也在飞速的发展,它的门槛和复杂度越来越高,使得招聘优秀的工程师变得更加的困难。
业务增长和人员短缺之间的矛盾导致许多中后台产品的产品体验和研发效率是存在一些问题的。而且有非常多经典的著作都阐述过,软件研发领域存在的这些问题是很难通过增加人力投入或者某种神奇的管理手段就能完美解决的。
那么云凤蝶尝试解决上述问题的主要思路是什么呢?
我们可以尝试跳出软件研发的 Scope 去跨界看一下别的领域,比如制造业。
当下制造业的全球分工和产业链整合已经达到了一个空前高效的地步,典型例子如 iPhone。iPhone 有200多个零部件供应商,总共 400 多道组装工序,而富士康的郑州工厂却可以达到一天50万台 iPhone 的恐怖产能。
这里有一些核心秘诀,首先是要标准化专业产出和沉淀,也就是那些高精尖的零部件生产,然后要极力优化上下游协作关系和效率,同时要保持简单高效的组装流程。
我们尝试将这个思路推广到 Web 应用研发上来。在 2020 年的今天,面对一个中后台业务,无论你的公司是 React、 Vue、 Angular 技术栈,应该极少数场景你才会选择直接基于 html 的 span div 来从零开始。一般来说大家或多或少都会选用一个开源组件库或者维护一个自己的业务组件库,也就是说当下的中后台 web 应用研发已经基本变成围绕一套组件体系的拼装与组合了,这也是一个研发体系趋于成熟的标志。
那么云凤蝶的核心思路就是将组件生产和组件组装这两部分工作进行职责分离。云凤蝶会专心去做一条高效的组件组装流水线,同时打通 npm 组件的一键导入流程,从而完成一条产业链式的分工协作,最终实现规模化的快速生产。
总结来看,云凤蝶要做的事情可以总结为两句话,就是设计的标准化和研发的工业化。
这两个命题在普适化的场景下的确看起来很难,但是如果我们将目标限定在中后台领域下,则会变得稍微可行一些。
因为中后台产品某种程度上它就像是传统数据库管理软件的下一代延伸,它主要关注的是数据如何被高效查询、展现与修改。它一般并不会去追求炫酷的设计或极致的性能。它的设计通常是可被高度收敛的,表格、表单、图表、信息卡片等这些可以被模式化,而且蚂蚁的 Ant Design、AntV、 TechUI 等这些产品一直都在尝试推进设计标准化这件事情。
上面我介绍了云凤蝶是一个标准化的中后台产品快速研发平台,其主要手段是可视化 + 低代码的组件拼装与组合,那么这个思路有没有先行者呢?
当然是有的,日光之并下无新鲜事,早在 97 年 Dreamweaver 这个产品就尝试过所见即所得的编辑模式,2006 年微软的 WPF 也已经已经开始使用拖拽、数据绑定这些技术来开发 GUI 应用了。虽然这两个产品由于各种各样的因素逐渐在历史的长河里面被遗忘,但同时又有许多新的浪潮涌现出来。
比如我们去看低代码快速研发平台领域,竞争更是激烈,除了老牌的企业级软件服务厂商 salesforce,新兴的包括 Google 在做的 App maker,微软在做的 PowerApps,还有优秀的创业公司 Mendix 等大有人在。
甚至在移动 APP 研发领域其实也有点在往前端社区同样的趋势演进,了解过 ios 开发的同学可能知道 XCode 很早就有 storyboard,而 Apple 在 2019 WWDC 年正式发布了他们号称研发了三年的 SwiftUI,使用了声明式 UI 和响应式数据这两个前端同学非常熟悉的理念,搭配有限能力的视图拖拽和一整套跨平台的组件库,试图将整个 Apple 平台的应用开发体验演进到下一阶段。
第一部分我们了解了云凤蝶是什么,我们为什么做这件事情,以及整个行业趋势是怎样的,下面我会重点分享一下云凤蝶整个可视化搭建的底盘是是如何实现的。
我的同事绯一在知乎专栏上写过一篇文章分享了一个可视化搭建系统的演进过程,他分享了一个有意思的观点:做这样的产品它其实有点像画一匹马,虽然骨架清晰,但细节巨多。今天我们由于时间限制就不会非常深入地展开探讨细节,主要会关注整个骨架如何建立起来。
但是在深入到具体的技术方案之前,我们需要先对一个概念达成共识,就是何谓 “组件”?毕竟云凤蝶核心是围绕组件的,上面我也一直在提组件这个名词,那么到底我所说的一个“组件”是指什么?
在当下的前端框架体系下,比如 React 下面,组件可以抽象看做一个函数:f: props => VDOM,它的入参是一些抽象好的配置,也就是说 props,它的返回值是用某种协议去描述的 ui,比如说 React 使用的 Virtual DOM 协议。
当然,在今年 React 的 suspense、 hooks 这些特性出来之后,就连函数式组件也不再那么纯粹了,但是这并不影响我们今天的讨论,我们下面仍然以函数式的组件抽象去进行今天整个分享的探讨。
那么一个组件化搭建平台其核心骨架由哪几个步骤组成呢?
那么接下来我会主要以这五个步骤为线索来给大家分享一下。
首先为什么云凤蝶要做组件导入?因为如果用户可以自由导入组件,那么就说明云凤蝶其实是一个开放的组件体系。
而大家如果有过类似产品使用经验的话可能会了解到,不少这类产品都是采用封闭式的组件体系,什么叫”封闭式“呢?意思是指你只能使用平台的内置组件去实现功能,一旦平台提供的内置组件不满足你的需求,可能你就束手无策了。当然有些平台可能会给你一份开发者文档,说请你按我的开发规范来开发一个你要的组件,那这个成本其实是比较高的。
而云凤蝶期望的生产关系则是反的,我们不希望用户为了云凤蝶去开发组件,我们希望能复用整个 npm 世界的已有成果,毕竟在蚂蚁我们已经有 antd,antv,techui 这些优秀的组件资产了。因此云凤蝶一开始就做了职责分离的决定,由云凤蝶去导入和消费传统编码所生产的组件,形成一个上下游的产业链式的协作关系。
下面这个视频 Demo 就是演示如何在几分钟之内将一个 npm 组件导入到云凤蝶上来使用。
大家在日常写代码的时候,想要某个模块或者组件自然会想到去 npm 上搜索,那么云凤蝶也一样,直接输入 npm 包名开始搜索。
比如视频里面输入了 antd 这个 npm 包名,云凤蝶立刻启动一个云端任务,开始实时分析和抓取到这个 npm 包内包含哪些组件。然后你勾选你需要使用的组件,云凤蝶又会帮你抓取分析到这个组件的 props 描述信息,你可以二次补充或者修改一些中文文案之类的信息,在这个过程中你还可以实时预览到我们生成的属性配置面板的效果,然后你就可以把这个组件拖入到画布里面正常使用。
那么上面这个组件导入功能是如何实现的呢?秘诀其实就是上面提到的背后的那个云端任务,一方面云凤蝶会采用 TypeScript 当做 Parser 来分析 npm 包的类型信息得到组件元信息,另一方面云凤蝶会用 Webpack 来二次构建你的 npm 包得到一个可执行的组件入口文件,最终运行时则会采用 SystemJS 来加载这些组件,并且灌入用户的配置数据完成渲染。
通过这一套组件导入体系就保证了云凤蝶跟传统编码始终是打通的,所有生产资料可以直接复用,取之不尽用之不竭。
OK,那第一步完成了组件导入之后,接下来我们需要用许许多多的组件来拼装组合还原设计稿了。
而不同的产品在这一步就会体现出巨大的理念差异。
第一种流派是 DSL 流派,也就是像下面的 SwiftUI 这种的,半边屏幕是写代码的,半边屏幕是拖拽视图的,两者之间双向等价转换,用户可以按自己的口味选择。
这种方式的缺点是它对用户的专业技能要求高,学习成本比较高,不过优点是一旦熟练之后,确实效率杠杠的,因为本质上就是在写代码了。
第二种比较常见的方式是采用 Flex 流式的编排或者 Grid 栅格网格式的编排。这种模式的优点是在特定的垂直场景下非常高效,比如下面这种一行一行排版的 Form 表单 ,但是这种模式的缺点是它适用的场景比较有限。
第三种模式呢,就是类似设计师用的 Sketch,PD 用的 Keynote 那样的自由编辑模式。云凤蝶采用的就是这种自由画布的模式,为什么我们会如此决策呢?
主要原因就像下面这个 Sketch 的操作视频演示的一样,这种自由拖拽,随心摆放的模式,屏蔽了专业前端的布局知识概念,更加契合普通用户的心智,那么它让设计师、PD 这一类角色直接参与到研发链路上来成为了可能。这个想象空间是非常巨大的,毕竟在 Ant Design 设计体系如此强大的前提下,为什么一定还要求 PD 用 Axure 画一遍原型,设计师再用 Sketch 画一遍视觉交互稿,前端最后再用 HTML + CSS 画一遍页面呢?
那么下面继续进入 Demo 时间,下面这个视频是演示如何使用云凤蝶来制作一个自适应页面。
核心步骤其实就是拖入组件来摆放好,按需配置一些属性即可。可以看到这个体验就像设计师用 sketch 来画设计稿,PD 用 keynote 做 PPT 一样简单自然,而且最终生成的页面还是自适应的,当屏幕变大变小的时候,该靠左的按钮,该靠右的按钮它们仍会保持约束的关系。
那么上面这样一个自由拖拽的画布其实一只脚已经进入到了设计类软件的领域,另外一只脚没进去则是因为,相比设计类软件,我们更关注的并不是基于点线面色彩的自由创造的表达力,而是如何用现有组件资产去按照设计规范快速拼装组合来还原设计稿的能力。
即使只有半只脚在做设计,这里面缩放,拖拽,对齐,测距,吸附,分布等等细节还是非常之多,而且这些细节的体验可能需要打磨得非常好才能得到效率提升,不过由于时间原因今天不会展开讲这些细节,感兴趣的同学可以阅读相关分享:
自由画布除了体验上细节非常多之外,更重要的是它还需要一些非常核心的算法原理来弥补从设计师视角到工程师视角的 Gap。举例来说,从自由拖拽的画布如何识别出一颗组件树呢?
大家知道设计师在 Sketch 里面摆放元素的时候 TA 是不关注什么父子兄弟关系的,设计师只关注二维平面的位置。但是云凤蝶也并不想让我们用户人工手动的去维护父子关系这些信息,这个对用户的成本太高,我们希望自动帮 TA 推断出来。
云凤蝶会依靠“位置直接被包含”的一个算法去识别出组件树。这个算法的最核心原理就是基于一个非常简单的判定:每个组件都是一个矩形盒子,如果一个组件的矩形盒子面积直接被另一个组件的矩形盒子包含了,那我们就认为它们是直接父子关系。
第二个问题是自由拖拽摆放从用户的感受上来说是它绝对定位的,那么运行时如何转换为 CSS 的相对定位呢?毕竟在运行时我们还是要还原成一个普通的自适应的 Web 页面的。这里依赖的原理则是另一个“行列格式化”的算法,它是指将一个页面里面摆放的所有元素进行从顶向下递归的行列切分,像切豆腐一样,将一个巨大的页面分割为一个一个的小行列,这样大的绝对定位就化解为小的局部的相对定位了。
经过前面两步,组件已经导入进来,也已经拖拽摆放到了合适的位置,那么接下来自然就是根据业务的需要去配置组件的属性了。那所有这一类产品大家必不可少都有一块属性配置面板,就像图上展示的云凤蝶有基础属性面板,也有高级属性面板。
不过即使你将属性面板的体验打磨到极致,也只能实现和写代码的情况下同等的能力,如果想要得到比手写代码更高的效能提升,那平台就一定要具备扩展能力。
如何扩展?我们抽象来看,一个组件的 props 配置数据说到底其实就是一颗对象树 ,针对这棵树,云凤蝶核心的扩展思路有两条,分别是“横向”与“纵向”的扩展这棵树。
第一类所谓“横向“扩展,就是指云凤蝶会用 HOC 高阶组件的原理去包裹原始组件,给它附加非常多它本身并不具备的一些通用高阶能力,比如链接跳转,悬浮提示,loading,条件渲染,重复等等。
这每一个能力其实都是 Web 研发中那些高频的通用功能,云凤蝶会用一个个 hoc 将它沉淀下来,并且在产品上体现为属性面板上一个配置,从而达到提效的目的。
第二个所谓”纵向“扩展,是指云凤蝶采用了一套实体 Loader 的机制去扩展属性值类型,也就是在云凤蝶属性面板上,你除了可以配置 JavaScript 基本类型,比如字符串,数字等等,还可以配置许多特殊的”实体“类型,如:
云凤蝶的 Runtime 也就是运行时,会递归地扫描组件的属性配置数据,调用对应的实体 Loader 进行翻译。
导入组件,拖拽组件,配置组件,到这里如果不考虑世界上的电脑屏幕尺寸大小不一的话,其实我们基本已经可以下班了,工作已经基本完成了。可惜世界上的屏幕设备确实尺寸不一,当然中后台会比移动端简单一些,因为至少没有水滴屏,刘海屏这些”异形“屏幕问题。
不过无论怎么说, Web 应用是不能说像 PPT 投屏一样去直接等比缩放的,我们必须引入一套布局体系来描述当屏幕尺寸变化的时候,我摆放的组件与其他人的位置关系该如何变化。
云凤蝶在调研过后设计了一套类似 ios AutoLayout 的规则系统,但是在具体规则设计上做了进一步的简化,一个组件最终只剩下它自己本身的宽度高度,以及它与整个世界的上下左右间距关系六个配置维度。
每个维度只剩下固定,适配内容,自适应三种配置模式,其代表的意思也非常好理解。
这套布局体系的具体实现算法由于时间限制今天不会展开细讲。但是需要解释的是为什么云凤蝶不直接采用 CSS 布局体系?
我们主要的考虑是因为两点:
前面四个步骤,组件导入,组件拖拽,组件配置,组件布局,我们的视角一直在关注组件,现在我们是时候把视角从单个的组件拉大到整个页面的维度了。
当我们去看一整个页面的时候,会发现组件并不是孤立的,页面里是存在着不少组件之间共享状态与联动的。
比如常见的例子:页面上并排有三个 select 下拉选择,第一个 select 选了某个值之后你希望第二个 select 根据第一个的值做一些变化,这种需求如何实现呢?
我们横向去看前后端,要实现两个东西关联无非两类方式:
在当下的前端领域,状态外置确实也是主流的思路,redux,dva,mobx 都是如此,把所以状态分门别类的放在一个公共存储上,不同的视图模块按需的 “读”,“写” 这些状态,从而完成共享与通信。
云凤蝶也选择了状态外置的模式,我们会为每个页面提供一个 Model 文件,但它其实就是一个普普通通的 JavaScript class 而已。而 class 的核心概念正好和状态管理美妙的一一对应,比如 class 成员变量对应状态,class 成员方法对应修改状态的行为,这在形态上有点像后端的充血模型,只不过视图状态并不是业务模型,它一般比较凌乱且迭代比较快,无法像后端领域模型一样抽象得那么稳定和纯粹。
说回到云凤蝶的 Model Class,你对云凤蝶的 Model class 内任何属性的修改不需要任何特殊手段,没有 immutable,没有 setState 这些 API,就直接大道至简,回归到 JS 赋值修改的本源即可,这也是非常爽快的一件事情,当然在超大型的项目里面这样做会导致少了一些工程化的约束,这一点我们暂时不做展开讨论。
那么当定义好页面的 Model Class 之后,用户可以通过云凤蝶的属性面板将组件的某个属性的值绑定(或者说关联)到 class 内的成员上,比如将:
通过属性面板可以完成模型和视图的关联,注意这里的关联用上的就是我们前面讲的,云凤蝶的属性面板不仅可以配置 JavaScript 基本类型,还可以配一些特殊实体的纵向扩展能力,这里的数据绑定就是云凤蝶的一种很重要的扩展。
并且更美妙的是,云凤蝶采用的是和 Vue 类似的响应式架构,那也意味着一旦你的组件属性所绑定的 Model 变量发生了变化,云凤蝶的 Runtime 会自动的帮你精确更新该组件视图,你完全不需要再操心更新这件事情。
具体响应式的原理,由于在社区已经有非常多成熟的解决方案,大家感兴趣的话可以阅读这些开源库的实现:
但是我还要额外解释一下 Why?为何我们选型这种状态管理方式? 响应式理念有什么优越性吗?
我认为它有一个很重要的意义就是自动化。
可能大家会认同说学习 JS 比 C/C++ 简单一些,我觉得有一个很重要的原因大概是因为 JS 不需要手动管理内存,那么现在在业务开发里面,其实我们也不想手动管理依赖。
那什么东西叫做依赖呢?下面这两张图是来自 2019 年 wwdc Apple SwiftUI 的发布稿 ,它是在说 GUI 应用里面有非常多的视图与状态甚至状态与状态之间的依赖关系要处理,比如浮层的开关,表单的校验是否通过等等,这些依赖关系会发散成一张网,最终一旦这个依赖关系的复杂度超越了程序员大脑的承受能力,程序员的大脑可能就会”爆炸“,然后就会有 bug 的产生。
而响应式的理念是指你只需要声明出依赖关系,之后的管理交给框架来做。
而且这个声明并不需要大动干戈,你只需要在描述业务的过程中顺带声明依赖,比如你在渲染视图的过程中读取了哪些状态变量,我们就可以认为这个视图依赖了这些状态变量。
状态外置的另一个优越性在于,当我们将状态全部外置之后,得以将组件可能会发生更新的因素全部隔绝在外置状态的这一侧了,于是我们就找到了一个精确的性能优化切面。
因此云凤蝶的 runtime 可以选择隔绝掉所有父子组件的级联更新,因为在云凤蝶体系下这根本不可能发生。 比如 react 体系下云凤蝶就可以使用 shouldComponentUpdate false 来优化性能,让我们可以得到比手写代码更高的更新性能,毕竟在手写代码的情况,要想实现同等效果你是需要非常小心的手动去 take care 的。
讲完上面五步的核心技术实现之后我们再来回顾一下,做一个组件化的搭建平台,其骨架分为:
这样我们基本上就画了一匹马了,至于完善这匹马的毛发细节,由于时间原因就暂时不展开。
好的,前面两部分已经概览性地的介绍了整个云凤蝶的产品设计和技术实现,那么下面我们简单看一些云凤蝶的用户案例,然后展望一下未来会是什么样子。
下面这个案例是云凤蝶本身控制台的页面,它实际上就是用云凤蝶编辑器搭建出来的,也就是我们完成了一个有意思的自举。
下面这个是云凤蝶的品宣页,它则是完全由设计师动手搭建的,无需开发参与,这可能会是多年来前端同学最头疼的设计还原度问题的一种究极解法。
下面这个是使用云凤蝶搭建的经典的浅色主题的 CURD 中后台,表格,表单,侧边栏等,这可能是我们最主要覆盖的用户场景。
下面这个应用本身是一个历史老应用,不过其中有几个迭代的新需求选择了使用了云凤蝶搭建,然后使用微前端技术将云凤蝶搭建的页面与手写代码无缝融合,也就是说这两种研发模式并不对立,反而是一种互补,在不同的场景下你可以因地制宜选择不同的研发模式。
好了,讲完了云凤蝶的产品,技术,用户案例,可能不少人内心还是会萦绕着一个隐隐约约的担心。
那就是可视化搭建看着不错,这么多年无数人也都在做,但好像天花板就在那了?做到头也就那样?能真的用起来吗?
这个问题非常在当下确实非常难以回答,因为对于一个未被证明的模式,未来的事情谁也说不清楚。
但我可以先给大家看看,在云凤蝶这里,在我们的可视化搭建的这颗底盘上可能还会生长出怎样有趣的果实。
我们举个例子,用一个前端同学日常工作中可能会发生的小故事来看。
假设你老板对你说,”我们这个 XX 区块链系统的后端已经搞定了,这周把界面撸一撸,我们拿出去卖!“。你一看,又是这种千篇一律的 CURD 后台,你内心可能是拒绝的。当然你肯定没法拒绝,于是只能开始撸代码了...
不过动手之前,你首先得技术选型,下面这是 dva/umi 的作者云谦给的一个前端决策树,包括框架,语言,测试,路由,构建等方方面面,你需要根据自己的团队和业务现状选择合适的武器库。然后你还需要再搭个脚手架,装个依赖,跑个构建,还要再规划整体布局和设计路由,可能一天过去,你还并没有写下第一行业务代码... 这是非常伤心的一件事情。
而让我们看看假设使用云凤蝶,怎么来做需求。因为你的后端接口已经 ready 了,下面这个视频是演示如何通过云凤蝶在几分钟之内从后端的接口快速生成一个复杂的表单。
主要步骤是你选择一个 POST 或者 PUT 类型的接口,云凤蝶会识别这个接口的 request 参数格式与数据特征,基于对数据特征和设计规范的理解,云凤蝶会完成一个大致的推定,自动帮你生成对应的表单项,比如 number 数据类型可能使用 InputNumber 表单项,Date 类型使用 Calendar,诸如此类。
当然在这个过程中你可以干预配置,也可以实时预览效果,生成完了之后你可以直接上线,也可以基于云凤蝶编辑器继续二次修改与迭代。
上面这个核心的原理其实比较纯粹,背后基于对数据特征和设计规范的理解,云凤蝶可以直接完成从数据到视图的推定,那么同理云凤蝶也可以从后端接口快速智能生成 Table,列表,图表等其它视图类型,比如下面一个视频 Demo 就是演示如何使用云凤蝶智能向导根据接口快速生成表格。
主要流程是选择一个 GET 类型的接口,云凤蝶会识别你的 response 是否包含数组类型的数据,根据数组项的字段结构生成表格列,过程中你可以进行更细化的配置,也可以在右侧实时预览你的配置生成的视图效果,生成完了之后你可以直接上线,也可以基于云凤蝶编辑器继续二次修改迭代。
那么当一个页面最复杂的 Table 和 Form 都已经被自动化生成了之后,这个效率的提升将会是巨大的,而且这个生成并不是一个空架子,它是包含完整的业务逻辑的,同时它也不是一锤子买卖,你仍然可以继续整个云凤蝶的可视化编辑器去做产物做持续的功能迭代与二次修改。
从上面演示的云凤蝶智能向导根据后端接口智能推导生成视图这个功能,我们隐约可以感受到一个能力完备的可视化底盘的威力。
在我看来,可视化搭建本质是先去抽象,解构一个 Web 应用的方方面面,再尝试用一套自己的描述方式去重建,在这个过程中实则让我们拥有了对一个 Web 应用更上层的理解,描述,甚至是操纵能力。
具体来说浏览器理解的是程序语言,前端框架理解的是 API 设计和框架规约,而云凤蝶得益于高度抽象与管控的研发体系,我们得以更深入更全面的理解了应用的方方面面,从而可以尝试做更多事情。
那么未来云凤蝶还可能做哪些有意思的事情呢,首先在 API/数据/模型驱动这类自动化大幅提升研发效率的功能上我们会继续深挖,然后在更多智能化的场景我们也会进行探索,然后包括前后端研发,资产体系,工程化等等整个应用研发链路方面也会继续去完善打磨。
我的分享就到这里,对更多云凤蝶产品和技术细节感兴趣的同学加群向我们提问与交流,谢谢大家~
https://qr.dingtalk.com/action/joingroup?code=v1,k1,dhuiHpqntK0gDuKAXdiInwDeZyaGvpQQX8j1LC/bW7E= (二维码自动识别)
我的演讲视频和 PPT 都已经上传到:SEE Conf 的语雀专栏,欢迎下载,同时也欢迎到 知乎问答 跟我互动。
SEE Conf 是蚂蚁金服体验科技大会,是一个“看见者的大会”,希望技术能看见设计的价值,希望设计能看见技术的力量,彼此看见中互相融合成长,一起让世界更美好。
其他相关资料: