跨端框架 RAX 初体验
锵锵锵~ 小伙伴们,新出的会议室预约系统用着还顺手吗?快告诉我,对比之前的古老预定方式是不是方便很多?系统响应快不快?界面是不是一目了然?交互方式友好吗?
看着每天预约人数这么多,无疑答案都是肯定的。
那你有没有预约个会议室来把玩一下?预约网红会议室像不像淘宝双十一的大抢购?悄悄告诉你,会议室预约系统采用的前端框架,就是淘宝双十一的 Rax。爱学习的你一定嗅到了知识的味道~
那什么是 Rax,怎么用 Rax,什么时候用 Rax 呢?接下来,前端女同学就来聊聊会议室预约系统的前端技术使用。
1. What
什么是 Rax 呢?来看官方定义
Rax 是用于构建通用应用程序的渐进式 React 框架,其内部提供基础UI组件。目前,在阿里系公司展开使用,并在淘宝双十一购物节中独领风骚。
通过上面定义我们知道,Rax 是基于 React 标准,并支持在不同容器中渲染(当前最重要的容器即 Weex 和 Web )。
核心思想两个 “ React ” 标准和 “ 跨容器 ”。别问为什么,先记住,快,我知道你要问什么;因为接下来我要讲 Rax 和 React 的关系了,听完你就没有疑惑了。
1.1 Rax 和 React
我们可以把 React 看作是一种标准,那么Rax 就是对该标准的一个跨容器框架的实现。也可以说 Rax 只是 React 的无线端的解决方案,与 React 并无冲突,我们就把 Rax 看作是一种扩展,因为 Rax 与 React 还是有一定的区别。
比如:Rax 没有 createClass() 方法;Rax 可以返回多个同级结点,React 只能有一个结点;React 中的生命周期在 Rax 也同样可用,一些副作用和钩子函数均可使用。说到这些,你一定会想到 state,认为 state 状态的设计一定也一样,其实暗藏玄机(故意神秘)。
1.2 setState 不同
常言道: 结论先行,来,上结论。setState(updater, callback) 在 React 中是异步/同步的,而在 Rax 中是同步的。
先一起回顾一下 React 中怎么处理 setState 问题。
1.2.1 React 的 setState 机制
在 React 中,setState 后通常会集齐一批需要更新的组件,然后一次性更新来保证渲染的性能。
这也是通常操作,React 里面有两个更新 state 的方式,同步和异步。如果是由 React 事件引发的 setState(如 onClick 和钩子函数),调用 setState 是异步更新,除此之外的 setState 是同步执行(如 addEventListener 和定时器等)
React 会把每一次的 setState 操作放入一个队列里面,会先判断是否由 React 事件引发的 setState ,来判断是直接更新还是批量更新。
- 先对 React 事件进行标记,将 setState 传入的 state 参数存储在当前组件实例的 _pendingStateQueue 中;
- 将 React 事件引发的 setState 更新放入异步队列中并 dirty 标记,需要批量更新 state,把 VDom 到 Dom 的操作降到最小;
- 非 React 事件引发的 setState ,进行同步执行直接更新;
1.2.2 Rax 的 setState 机制
Rax 的 setState 为同步更新。只要调用 setState 就会直接更新对应的状态。
然而 setState 是会引起视图的更新会引发重绘。也就是会重新走一遍更新阶段的生命周期,这样一来势必会带来性能问题,这时候就需要开发者需要控制好更新时机,要不然系统就会卡卡卡卡卡卡。也是正因如此,Rax 对开发者的要求略高。
1.3 Rax 特点
- 设计上支持不同容器;Rax 在设计上尽量抹平各个端的差异性,这也使得开发者在开发中,差异性和兼容性方面再也不需要投入太多精力。
- 体积足够小;Rax 是一个面向无线端的解决方案,因此自身的体积对于性能来讲就显得非常重要。Rax 压缩 + gzip 后的体积是 8.0kb, 相比 React 的体积, 对于无线端很友好。
- 支持返回多个同级节点;这一特性可以有效减少页面的嵌套层级,从而减少应用因嵌套层级过多而出现的 crash 问题。
- 标准化;需要适配各个端,那么需要各个端的一致性,一致则必有规范可依,目前 Rax 遵循 W3C 标准,受限于各个端的差异,「更标准化」这也是 Rax 未来的重要目标之一。
1.4 多端运行机制
来,先上图
Rax 的跨容器特性是通过抽象出 View 层来实现的。
Rax 将 Vdom 与 Dom之间的转化抽象出 Driver,依靠 Driver 来驱动实现跨端。
Driver 定义了 VDOM 在具体容器下的渲染实现。比如在 Web 场景下,对应的 Driver 为 Driver-Dom,它描述了在浏览器中,如何将 VDOM 渲染为真实的 DOM。
正是基于这种思路,同一套代码,经过不同的 Driver 就可以运行在不同的容器下。
2. How
2.1 创建
快速创建一个 Rax 多端应用
npm init rax todoList
2.2 初始化
初始化项目过程中, 按照项目的需要选择配置信息。以下是简单 todolist 的 demo 演示所需要的配置项。
2.3 项目目录
初始化的项目后,得到如下项目结构
├── README.md # 项目说明 ├── build.json # 项目构建配置 ├── package.json └── src # 源码目录 ├── app.js # 应用入口文件 ├── app.json # 应用配置,包括路由配置,小程序 window 配置等 ├── public # (可选)静态资源目录,会拷贝内容至 build 目录 ├── components # 应用的公共组件 │ └── Logo # 组件 │ ├── index.css # Logo 组件的样式文件 │ └── index.jsx # Logo 组件 JSX 源码 ├── document # 页面的 HTML 模板 │ └── index.jsx └── pages # 页面 └── Home # home 页面 └── index.jsx
2.4 路由配置
Rax 使用 rax-use-router 来管理多个页面,生成的 Rax App 是一个单页应用(可通过 build-plugin-rax-multi-pages 切换成多页应用)
- 配置:路由的配置如同小程序的配置,在 app.json 中直接写入;
- 使用:路由正常使用,动态路由和路由对参数,不能直接从 props.id 获取,路由信息需要自己解析,不能像 react-router 那样。路由的配置信息如下图所示。
2.5 状态管理
状态管理方面可以采用和 React 相同的状态管理库,大致分为两类:
第一类:搭配中间件 Redux 状态管理,为了简化开发关注点,对 Redux 进一步封装,例如:Dva 等等;
第二类:采用 observerble 的方案,例如:MobX 等;
如果使用状态管理的业务场景较少,可以考虑 React 的 Context ,官网推荐的 hooks 中 useContext + useReducer 搭配使用。具体的使用方案还是要看具体的业务需求。
2.6 组件传参
父 => 子:父组件可以通过 props 给子组件传递数据;
子 => 父:子组件可以通过 callBack 的方式调用父组件;
兄 <=> 弟:对于兄弟组件或者跨层组件,可以通过发事件的方式通信,也可以单独引入状态管理框架来处理。
当然了,也可以拿到组件的实例,使用 ref 直接调用相应的方法。
2.7 UI 组件库
Rax 的社区不够活跃,几乎没有可用的第三方 UI 组件库,仅有官方提供的一些常用功能组件,三大类组件库如下:
- 基础组件:如 View 视图组件,默认 Flexbox 布局,可任意嵌套;
- 基础容器:如 ScrollView 滚动容器,设置确定的高度展示列表内容;
- 功能组件:Embed 内嵌内容容器,在 Weex 容器中通过 <web> 实现,在 Web 容器中通过 <iframe><embed> 实现;
2.8 打包
- 在 build.json 中选择一个或多个需要投放的端,目前可供选择的有 Web、 Weex、Alibaba Miniapp、WeChat MiniProgram、Kraken (Flutter);
- 运行 npm run build 命令即可按需生成 build 打包文件。
code端miniapp阿里系小程序(支付宝小程序、天猫精灵小程序)webweb 页面weex容器wechat微信小程序kraken一款轻量级的面向前端网络开发人员的框架
2.9 踩坑
(1)多利用 PureComponent,StatelessComponent 优化组件渲染
Rax 把很多处理性能优化的工作交给了开发者来处理,这也是 Rax 对开发人员要求更高的一个原因。PureComponent 在更新触发时会比较 props 和 state,如果没变化就不更新。StatelessComponent 在组件渲染时不会生成 Component 实例,能减少一定性能开销;
(2)尽量控制好 Dom 的更新时机;
Rax 的 setState 是同步的,肯定需要避免频繁调用,最好是数据都统一更新,自己手动调用 forceUpdate 更新 Dom;
(3)子组件适当提供 Key,尽量保持组件 Dom 结构的稳定;
子组件设置 Key 和 Vue 原理类似的,为了保持 Dom 结构稳定,当然也有利于虚拟 Dom 的 Diff 算法,可以避免频繁的 Dom 操作;
(4)Rax 中页面宽度默认是 750rem,无单位可与 rem 单位等价替换,各端兼容;
(5)路由传参,不支持 props.id 直接获取,路由信息需要自己解析;
3. When
Rax 优点:跨容器、高性能、轻量。
但是 Rax 本质上还只是属于 UI 层模式,在接入时应该注意和自己的底层框架解耦。并且 Rax 对开发者的要求较搞,如果没有一定的经验和规范约束,容易导致项目臃肿、结构混乱的问题。
目前在移动端 React 和 Vue 都有很高的占比,社区生态也都比较活跃。具体的选型可以结合团队的实际情况。
无论选择哪个框架,都需要认真研究框架原理,对框架的优缺点和潜在问题做到心里有数,如果能够掌握其运行机制就更棒了,这样在应对一些复杂需求和重大变化时才能游刃有余。