1. 背景
React Native(下文简称 RN)是混合应用领域流行的跨端开发框架。RN 非常适合灵活多变的电商领域业务,由于 RN 是基于客户端渲染的技术,所以相较于 H5 页面,它在用户体验方面有一定优势。
伴随着 Shopee 业务的飞速发展,我们 App 中的 RN 代码量增长得非常快,出现了构建产物体积过大、部署时间太长、不同团队依赖冲突等问题。为了应对这些痛点,我们探索了去中心化的 RN 架构,并结合该模型自研了系统(Code Push Platform,简称 CPP)和客户端 SDK,覆盖了多团队的开发、构建、发布、运行等一系列 RN 研发周期。经过近三年的迭代,现已接入多款公司级核心 App。
Shopee 商家服务前端团队打造了多款商家端应用,大部分用户是商家服务人员,他们对业务系统高可用和问题及时反馈有着很高的要求,从而也推动我们对 React Native 的架构有了更高的要求。
本文会从发展历史、架构模型、系统设计、迁移方案四个方向逐一介绍我们如何一步步地满足多团队在复杂业务中的开发需求。
2. 发展历程
随着业务高速发展,我们的 RN bundle 个数飞速增加,App 个数也达到近十个。整个 RN 项目在开发模型、部署模型和架构模型三个维度都发生了变化,从单团队发展成多团队,从一个 bundle 发展成多个 bundle,从中心化架构发展成为去中心化,最终发展成为每个团队的业务代码可以独立地开发、部署、运行。
整个发展历史分为 4 个阶段,分别是单 bundle 集中开发模式、单 bundle 多业务组开发模式、多 bundle 中心化发布模式、多 bundle 去中心化发布模式。
2.1 第一阶段:单 bundle 集中开发模式
最初的 RN 整体技术架构相对简单。由于当时业务形态不算复杂,为了满足独立团队在同一个代码仓库当中的开发流程,整个发布流程是基于 CDN 的更新发布,并且使用配置文件记录 RN bundle 文件的版本以及下载地址,以此进行资源管理。整个发布的产物有两个,一个是 RN 资源包,一个是用于资源版本管理的 JSON 配置文件。
每次 RN 资源在完成构建后,这两种构建产物会被放置在静态资源目录下。App 在特定的时间节点(例如 App 重启等)会自动拉取配置文件检查资源更新状态,然后再从 CDN 拉取 RN 静态资源。在下一次打开页面的时候,App 会加载最新的页面内容。
随着业务发展,越来越多业务团队期望使用 RN 技术栈开发业务,这种情况让已有架构发生改变,我们自然地产生了“多个业务组多个代码仓库”的想法。
2.2 第二阶段:单 bundle 多业务组开发模式
针对上述问题,多业务组的研发解决方案是 host-plugin 这种模式。
host 用于管理公共依赖和通用逻辑,它将 React、React Native、Shopee RN SDK 等通过一个独立的仓库管理起来,保证了特殊 RN 依赖的“singleton”(单例模式)条件,避免了部分客户端组件的重叠依赖,而这种重叠依赖是 RN 官方不允许的。
一个 host 对应着多个 plugin 仓库,业务代码仓库则是被看作为一个插件(plugin),以插件的形式接入主应用当中。业务团队可以按自己的编码规范来管理这个仓库。每个插件仓库会被视为 host 项目的 npm 依赖,它的构建是一个集中发布的流程。所有代码都会集成在 host 项目当中执行构建脚本。这种模式满足超级 App 的要求。
与此同时,host-plugin 的模式也带来了一个“难题”,业务发展使得 RN 产物体积逐渐变大,过大的产物会影响客户端的解压效率和 RN 容器加载 JS 时长。
2.3 第三阶段:多 bundle 中心化架构模式
针对 RN 产物体积过大的问题,我们利用构建工具将打包产物细分成多个 bundle,这一优化是非常有必要的,我们称它为“分包”。host 项目对应的是公共包,plugin 项目对应的是业务包。
整个构建发生在 host 项目,项目的模式还是“集中构建”和“集中发布”。多 bundle 产物将会发布到系统当中,客户端将拉取热更新的内容。客户端会按需加载对应的 bundle,RN 容器单次加载消耗