本文是第三届 SEE Conf 演讲的文字版


前言


大家好,我是来自蚂蚁金服的前端工程师 @边柳,很荣幸能在 SEE Conf 跟大家交流。


使用 React 开发小程序 for pdf.001.jpeg


为什么要用 React 开发小程序


大家知道微信小程序在商业上取得了非常大的成功,正是因为小程序在商业上的成功,导致后面不管是支付宝还是其他厂商在推出自己的小程序时,都参考了微信小程序的 API 设计。但是作为小程序的开发者来说,大家其实对于小程序的 API 设计都是很不满意的,所以现在小程序社区中有非常多的小程序框架,大家都希望能改善小程序的开发体验。


那我们为什么要选择 React 作为小程序的开发框架呢,当然是因为我们熟悉 React,我们希望能以非常小的学习成本去切换到小程序的开发上去。


另外 React 本身有非常庞大的技术生态,包括我们自己在 React 上有非常多的积累,我们希望能把这些积累运用在小程序的开发上。


使用 React 开发小程序 for pdf.004.jpeg


怎么样把 React 运行在小程序中


目前社区中是有一些 React 小程序框架的,不过这些小程序框架都有个共同点就是他们会自制一个 React 的轮子,另外他们都使用了静态编译的方式,所谓静态编译就是这些框架会把你写的代码解析成抽象语法树,然后通过语法分析把你的代码转换成可运行的小程序代码。


使用 React 开发小程序 for pdf.006.jpeg


举个例子,下面就是一个用某框架写的小程序页面组件,可以看到这个组件跟 React 组件非常像,你需要定义一个 Componentrender 方法,并且需要返回一段 JSX


使用 React 开发小程序 for pdf.007.jpeg


然后你的代码会被框架编译成小程序代码,他们会把 render 方法中的 JSX 部分提取出来,转换成小程序的静态模板,其他 JS 的部分则会保留成为小程序页面的定义。


使用 React 开发小程序 for pdf.008.jpeg


但是我们知道,JavaScript 作为一个动态语言,你想用静态的方式去分析它是非常复杂一件事情,我们只要稍微在刚才的例子中加入一点动态的写法,这些框架就可能编译失败。


使用 React 开发小程序 for pdf.009.jpeg


所以这些框架都会告诉开发者要去避免很多的动态写法。我们觉得要带着这么多限制去做开发,是否真的能提升开发体验需要打一个问号。那么除了静态编译还有别的办法去实现一个 React 的小程序框架吗?


使用 React 开发小程序 for pdf.010.jpeg


答案当然是有的。Remax 是我们最近打造一个 React 小程序框架,他的口号就是 “使用真正的 React 构建小程序”。


使用 React 开发小程序 for pdf.011.jpeg


这是一个使用 Remax 写的小程序页面组件的例子,可以看到我们没有再去造一个 React 的轮子,而是直接引用了 react ,你可以使用 React 所有的特性。


使用 React 开发小程序 for pdf.012.jpeg


我们是如何做到把 React 运行到小程序中的?先来看一点 React 的小知识。其实我们平常在用 React,大部分情况下都是在用 ReactDOM。ReactDOM 在整个小程序架构中为称之为渲染器,渲染器的作用就是把所谓的 React 的虚拟 DOM 映射到对应平台的真实元素上去,而 ReactDOM 的作用就是把虚拟 DOM 映射到浏览器的真实 DOM 上。另外还有针对移动平台的 ReactNative,以及社区有非常多的针对其他平台的渲染器


回到小程序上,我们要把 React 运行到小程序环境中,就是需要为 React 实现一个针对小程序平台的渲染器。而这个渲染器就需要 react-reconciler 这个包去实现。


使用 React 开发小程序 for pdf.014.jpeg


下面是一个用 react-reconciler 这个包去实现一个迷你 ReactDOM 的例子,我们需要给 ReactReconciler 方法传入一个配置,这个配置就是告诉 React 如何把虚拟 DOM 映射到真实的 DOM 上。


使用 React 开发小程序 for pdf.015.jpeg


通过配置好的 reconciler,我们就能实现一个 render 方法来渲染 React 组件了。


使用 React 开发小程序 for pdf.016.jpeg


这里只大概描述下实现一个 React 渲染器的基本原理,如果对这个话题感兴趣的同学推荐观看前 React Team 成员 Sophie Alpert 在 React Conf 上分享的《Building a Custom React Renderer》


开发过小程序的同学都知道,小程序中是没有 DOM 的,我们写的代码运行在一个与 DOM 隔离的独立线程中。Remax 在这里引入一层叫 VNode 的抽象层,我们会先把 React 的虚拟 DOM 映射到 VNode 上,然后把 VNode 转换成小程序页面的 data,然后在小程序模板里把这个 data 显示成界面。


使用 React 开发小程序 for pdf.017.jpeg


因为这个 VNode 就是 DOM 替代品,所以他长得也很像 DOM 元素,上面会有节点的类型(小程序中基础组件如:view、image、text),节点的属性和子节点,另外会有新增、删除和插入节点的方法。


使用 React 开发小程序 for pdf.018.jpeg


所以我们就可以用这个 VNode 去实现一个 React 渲染器了,可以看到我们只要简单把上面例子中的 DOM 元素替换成 VNode


使用 React 开发小程序 for pdf.019.jpeg


有了渲染器,我们就可以去渲染 React 组件了,把 React 组件渲染到 VNode 上,我们就那到一个可以完整描述界面的多叉树。我们会把根节点的 VNode 转换成 JSON,并把他设置成小程序页面的 data


使用 React 开发小程序 for pdf.021.jpeg


使用 React 开发小程序 for pdf.022.jpeg


接下来的问题就是我们怎么样把这个树结构的 data 在小程序的模板上遍历出来了。虽然我们没有选择使用静态编译的方式,但 Remax 还是会对你的代码有一次构建的过程,这个构建过程包括生成一个能遍历 data 的模板。这个模板会先去遍历 data 根节点下的所有子元素,然后根据子元素的类型,使用其对应的模板来渲染这个节点,如下图中我们会预先生成一个 view 类型的节点模板,用来渲染 view 类型的 VNode ,在节点模板中我们会继续去遍历这个节点下的所有子节点,然后再根据这个子节点的类型,去用对应的模板来做渲染。通过这种模板递归渲染的方式,我们就可以把整个树给显示到界面上了。


使用 React 开发小程序 for pdf.023.jpeg


好,到这里我们就完成了从 React 的虚拟 DOM 到 VNode,从 VNode 到小程序页面 data,再利用模板递归渲染 data 的过程,也就是把 React 运行到了小程序中并渲染出了界面。


使用 React 开发小程序


接下来我们就可以愉快地使用 React 开发小程序了。


TypeScript


我们选择 React 有另外一个很重要的诉求就是 TypeScript,我们希望能用 TypeScript 来开发小程序。当然即使你不用任何框架,你也可以用 TypeScript 来写小程序,但是在小程序的静态模板中你是没法享受 TypeScript 带来的类型检查和自动补全的。引入 React 的好处就是你不需要再写静态模板了,在 Remax 里,我们为所有的基础组件和小程序 API 都加上了类型约束,你可以完整的享受类型检查给你带来的安全感和自动补全带来的超爽体验。




CSS 预处理


小程序有自己的 CSS 实现,但我们依然喜欢使用 less ,Remax 支持社区中流行的 CSS 预处理器。另外支持 CSS Modules,让你不再需要为了想一个不跟别人冲突的类名而头痛。


使用 React 开发小程序 for pdf.026.jpeg


使用 React 开发小程序 for pdf.027.jpeg


状态共享


使用 React 开发绕不开的一个话题就是「状态共享」,小程序应用有点像是多页应用,多个页面之间的数据状态是隔离的。小程序本身把 App 设计成了一个单例,然后在这个单例上你可以去写各种全局变量。但我们知道全局变量是万恶之源,所以在 Remax 中,我们把 app.js 这个入口文件也设计成了 React 组件,所有的页面组件都会作为 App 这个组件的子组件来渲染,这样做的好处是我们把多个页面统一到了一个 React 实例里,你可以很方便地使用 context 去做状态共享了。另外如果你想使用一些状态管理库也可以直接使用,这跟你开发一个 SPA 的体验是一致的。


使用 React 开发小程序 for pdf.028.jpeg


使用 React 开发小程序 for pdf.029.jpeg


使用 React 开发小程序 for pdf.030.jpeg


数据获取


前段时间,React 社区有个非常火的 hook 叫 swr ,我们可以直接把这个 hook 用到基于 Remax 的小程序应用中,你可以非常优雅地去处理服务端数据。更完整的例子可以查看这里


使用 React 开发小程序 for pdf.031.jpeg


表单处理


前端开发绕不开的需求,大家知道在 antd 里有一个 form 组件,在 antd 4.0 里我们对 form 组件进行了重写,新的 form 组件有更好的性能,更重要的是基于 Remax,这个 form 组件可以用到小程序中。而这也正是我在前面提到的,我们希望能把在 React 上的技术积累运用到小程序的开发中去。


使用 React 开发小程序 for pdf.032.jpeg


引用小程序组件


前面一直在讲把 React 的技术生态引入到小程序中,但其实小程序本身发展了这么多年也有了自己的技术生态。为了能利用小程序本身的技术生态,Remax 还支持你在 React 组件中去引用一个小程序自定义组件。你不需要原来小程序中那样去声明一个 useComponents,直接把小程序自定义组件当成 React 组件去 import 然后当成 React 组件那样来用就可以。


使用 React 开发小程序 for pdf.033.jpeg


结语


以上就是我们在做小程序框架时的一些实践和经验,可以看到,除了把 React 引入小程序以外,其实我们更希望的是能设计一个对开发者友好、开放的小程序应用框架。欢迎关注我们的 GitHub 主页知乎专栏


视频 && Slide


我的演讲视频和 PPT 都已经上传到:SEE Conf 的语雀专栏,欢迎下载,同时也欢迎到 知乎问答 跟我互动。


SEE Conf 是蚂蚁金服体验科技大会,是一个“看见者的大会”,希望技术能看见设计的价值,希望设计能看见技术的力量,彼此看见中互相融合成长,一起让世界更美好。