查看原文
其他

【第1836期】Remax - 使用 React 开发小程序

边柳 前端早读课 2020-03-02

前言

Remax,竟然是第一次分享。今日早读文章由蚂蚁金服高级前端@边柳授权分享。

@边柳,蚂蚁金服前端工程师,Ant Design 核心贡献者

正文从这开始~~

为什么要用 React 开发小程序

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

数据获取

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

表单处理

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

引用小程序组件

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

关于本文 作者:@边柳 原文:https://www.yuque.com/seeconf/2020/qsytho

为你推荐


【第1831期】React团队的技术准则


【第1795期】SWR:最具潜力的 React Hooks 数据请求库


【第1834期】DCloud@崔红保:我眼中小程序的当下和未来可能

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

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