Rax App 研发框架背后的思考

1,498 阅读9分钟

前言

Rax 答疑群经常会有类似这样的问题:”我的 rax 版本是 1.1.4,想要使用小程序原生组件应该怎么做?“,从这个问题不难看出, 站在使用者的视角来看,Rax 发展到今天已经不再只有类 React DSL 驱动多端渲染这一单点特性,而是更加多元化的跨端解决方案,是更加立体的:DSL + 研发框架 + 辅助工具 + 平台。

一般来说,我们回复的第一句是:“你用的什么工程?可以看下 build.json 么?” 所以不难看出,目前 Rax 所提供的多端解决方案是和工程、研发框架密切相关的。

笔者从毕业到现在也参与过一些 Rax 多端相关的事情,包括淘宝小程序、Rax 转小程序、Rax App 研发框架等等。写这篇文章的目的其一是今年前端委员会跨端专项的部分成果总结、其二是希望可以让大家进一步了解 Rax 跨端框架在实现多端一致性的过程中经历了什么事情以及背后的思考与选择。

Rax App 研发框架

站在开发者视角,直接和他们打交道的就是 Rax App 研发框架。那么,Rax App 研发框架是什么呢?1. Rax App 提供了构建多端产物的能力,这也是开发者最本质的诉求;2. Rax App 提供了开箱即用的构建配置能力;3. Rax App 提供了项目开发的最佳实践;4. Rax App 会适配不同容器,尽可能提供多端一致的表现。

更为重要的是,和市面上很多多端方案不同的是,Rax App 并不会特别倾向于某一个容器的能力打造,我们的目标是能够让支持的所有容器性能表现和使用体验上都达到业界第一。

渐进式 React DSL 背后的思考

Rax App 本身是离不开 Rax DSL 的,站在当下的视角来看,Driver 的设计依然是先进的。哪怕是后来的 react-reconciler 其实都可以看到 Rax Driver 的影子。多端渲染的本质是渲染器的能力、绘制视图的 API,所以通过 Driver 可以很好的通过 VDOM 将业务逻辑本身和容器绘制能力解耦。

JSX 本身带来的是 React 丰富的社区生态以及业务使用的易用性、符合直觉的开发方式。进一步的为了简化部分场景业务使用的复杂度,Rax 提供了 JSX+ 的写法,可以通过 x-if 这样指令轻松完成条件渲染。但目前这样的写法对 TypeScript 支持还不太好,在即将发布的 TS 4.2 将会解决这个问题。

未来,Rax DSL 在和 React DSL 尽量保持用法一致亦或是子集的情况下,会有更多符合无线业务场景的判断与选择。

工程与多端解决方案

能力简介

Rax App 目前支持构建 Web/小程序/Kraken/Weex 产物,在 Web 上提供了包括 PHA、SSR 等多样的性能提升方案,小程序上提供了编译时、运行时双引擎方案,同时 Native 侧支持 Kraken、Weex。

小程序

小程序的能力,是前端委员会跨端专项中比较重要的一部分,在过去的 S1,Rax 本身着力提升了运行时方案的性能,并且提供了开箱即用的双引擎混用方案。

方案实现本文不再赘述,可以看以下文章:

笔者更多要介绍的是,在实现这些方案过程中,我们的思考是什么?以及遇到了什么问题,怎么解决的。

UI 资源引用

在 Rax 小程序的方案中,我们支持了运行时项目中使用原生小程序组件、Rax 编译时组件、原生小程序页面、小程序插件等能力,在编译时项目中支持使用原生小程序组件、插件、页面。

丰富的组合能力背后我们需要考虑的是,IDE 响应速度、用户体感使用速度、使用一致性等等。通过避免 json 配置文件重复写入大幅提升了 IDE 热更新响应速度。通过拷贝 package.json,自动安装依赖避免了由于支付宝构建限制导致的冗余安装流程。通过 AST 分析,自动寻找原生 DSL 写法帮助开发者可以直接通过 import Buttom from '../miniapp-native/Button/index' 写法引用组件。

双引擎混用与分包加载

在双引擎混合和分包加载的背后,除了工程构建之外,我们奉行『约定大于配置』的准则。比如在 miniapp-compiled 下放置通过 JSX 写的编译时组件;分包模式下 src/pages 下面的一级目录都是独立分包,同时还能构建出几乎同样表现的 Web 产物。

除了以上所说的之外,Rax 小程序工程上还做了很多很多的事情,我们真的很重视,业务方提出每一条关于使用体验、构建能力上的诉求,在这个庞大的体系之下,我们更希望能和开发 Web 一样开发小程序,寻求当下我们能想到的最优解。在业务纷杂的今天没有十全十美的方案,只有在不断权衡、取舍我们推荐的最佳实践。

Web

无线 Web 页面,也是截止目前 Rax 深耕最久的领域,我们提供了比React 更快的 SSR 方案 ,和 WebView 结合的 PHA 方案:通过框架能力提供类似多页 TabBar、横滑、缓存、Prefetch、同层渲染组件等能力,以及比较基础的快照、保活能力。

配置简化

搞前端最讨厌的事情之一就是配 webpack,得益于 build scripts 拉通了集团的前端工程构建体系。所以,Rax App 可以轻松进行类似如下配置:

alias
{
  "alias": {
    "@components": "src/componentns"
  }
}
minify
{
  "minify": false
}
babelPlugins
{
	"babelPlugins": ["babel-plugin-import"]
}

更多的使用可以看这篇文档。笔者所想表达的是,常用的构建配置 Rax App 几乎都有考虑到,你可以不用在意背后的实现,就轻松达到业务想实现的构建效果。

运行时体验一致性

笔者在这一节将以生命周期为例进行展开。

如果大家接触 React 多的话,可能对 componentWillMountcomponentDidMount 这些组件的实例的生命周期比较熟悉。完整的生命周期,更多的指的是什么?更多指的是一个应用的生命周期。

完整的生命周期可以做哪些事情?

  • 埋点:比如说在页面切换过来的时候,我需要上报一个曝光埋点。
  • 监控:比如说一些页面报错怎么收集,假设我们有自己的监控系统,怎么去看我们线上业务存在哪些问题。
  • 响应式的交互:比如从 a 页面切到 b 页面,假如 b 页面去做了类似登陆的操作,再返回到 a 页面的时候,我们需要有个 a 页面的生命周期来实现例如权限的一些视图变化,或是状态的切换等等。我们团队现在是提供了一套面向多终端框架的解决方案,希望可以让用户在开发中后台、PC、无线等多端应用时的开发体验是一致的,这样可以减少开发团队成员的上手成本。
  • 清理定时器:避免一些内存泄漏或者没必要的事件回调。
  • 获取启动参数:比如说在小程序里面,需要在应用启动阶段,如 onLaunch 阶段去获取一些启动参数等等。

所以基于上面这些需求,以及业务上的真实痛点,我们做了多端体验一致的生命周期,主要分为两部分:

  • App 的生命周期:包括应用的启动 onLaunch 事件,应用的报错 onError 事件,应用的 onShow 事件,应用的 onHide 事件。未来我们可能会加入针对 TabBar 点击的事件,如在点击 TabBar 时,需要做一些曝光埋点的能力,会针对 TabBar 的操作暴露出相关的生命周期。
  • 页面的生命周期:usePageShowusePageHide 等 Hooks,onShowonHide 等事件。

这里有同学会问说为什么 usePageShowusePageHide 这里称为 Hooks,而 onShowonHide 称为事件。这是因为在社区解决方案里面我常看到的 useXXX,很多其实就是 API 的调用,并不是真正的 Hooks,也就是说这种 API 的调用方式跟 Hooks 本身并没有关系。我们这里的 usePageShow 是真正的 Hooks,它跟 onShow 不同之处在于,usePageShow 的触发时机是在组件渲染完成之后,而 onShow 是在组件示例初始化的时候,也就是页面刚进来的时候,就会触发这个 onShow 事件。

无论是在 Weex,还是在 Kraken ,还是在小程序,或是在 Web 等等业务形态上,我们的方案表现都是完全一致的,包括触发时机和触发顺序,这样能够让你的业务逻辑在开发时保持完全一致。usePageShow 是在组件渲染完成之后,onShow 是在组件实例化的时候,也就是页面刚进入时。

总结:Rax 跨端框架的展望

Rax 的愿景,基于前端生态打通多端体系。在跨端体系的建设过程中,研发框架需要既要扮演构建器的角色,还要扮演拉平体验一致性的角色。未来,我们会通过 Rax App 研发框架提供更多的最佳实践,例如监控、埋点、数据请求等等,同时也会保持尽可能保持克制、稳定性,框架自身测试覆盖率、保证最后生成的 bundle 只包含用户需要的代码、探索新的方案提升构建速度等等。

最后笔者想说的是,Rax 的成长、发展离不开所有业务方的支持,感谢你们选择 Rax,同时感谢可能平时答疑被我们不小心冷落或者没顾及到考虑的同学,有你们真好,有你们 Rax 会更好。