使用 React Native 构建超级应用:经验教训
过去几年,超级应用已成为企业整合多种服务的一种流行方式。超级应用不会强迫用户下载和维护多个应用,而是提供了一个单一入口,让多个小应用或功能模块可以共存。
当我的团队开始使用React Native 构建企业超级应用程序时,我们的目标很简单:
- 使其具有可扩展性,以便可以轻松插入新模块。
- 确保它即使在离线环境中(如仓库或远程站点)也能正常工作。
- 保持开发人员的顺畅体验,以便团队可以并行工作。
这个博客分享了我们的历程、我们面临的挑战以及我们为使一切顺利进行而做出的架构选择。
为什么使用 React Native 打造超级应用?
有很多选择,但 React Native 对我们来说脱颖而出,因为:
- 跨平台支持:适用于 iOS 和 Android 的一个代码库。
- 丰富的生态系统:成熟的导航、状态管理和网络库。
- OTA(无线)更新:无需等待应用商店批准即可推送更新。
- 社区和工具:强大的开发者社区和像 Repack 这样的模块化捆绑解决方案。
离线模式:预取和启动策略
离线模式对我们来说至关重要。以下是我们的应对措施:
- 预取 bundles
- 在线时,应用程序会下载所需的包并将其缓存在本地。
- 捆绑配置存储
- 有关哪些捆绑包可用及其版本的元数据存储在本地。
- 离线启动流程
- 启动时,如果设备处于离线状态,应用程序会检查本地捆绑配置。
- 弹出窗口通知用户他们处于离线模式。
- 要求用户提供其EMP ID以继续使用缓存功能。
- 重新连接时同步
- 一旦设备恢复在线,就会下载更新的软件包并替换旧的软件包。
这确保用户可以继续无缝工作而不会出现连接问题。
在本文中,我们将深入探讨如何使用Re.Pack 的 Module Federation设计和实现 React Native 超级应用,内容包括:****
- ⚙️ 架构概述(主机、身份验证、平台、微应用)
- 🧩 通过
Federated.createURLResolver+进行动态加载Federated.importModule - 📦离线模式和 bundle 缓存
- 🔐 身份验证 + 登录流程联合
- 🚀 真实世界的模式和设置代码
🏗️ 高级架构
┌────────────────────────────┐
│ Host App │
│ ─ Auth Federation │
│ ─ Bundle Fetch/Cache │
│ ─ Offline Manager │
│ ─ Navigation Shell │
│ ─ Re.Pack Federation Init │
└──────────┬─────────────────┘
│
▼
┌──────────────────────┬──────────────────────┐
│ Auth App │ Platform App │
│ - Authentication │ - Shared Services │
│ - Offline Login Cache │ - Reusable Hooks │
│ - Token Federation │ - Realm/DB, APIs │
└──────────┬───────────┘───────────┬──────────┘
│ │
▼ ▼
┌────────────────────────────────────────────────┐
│ Remote Micro Apps (fetched OTA) │
│ ─ Dashboard, MicroApp1, MicroApp2, MicroApp3… │
│ ─ Fetched via federation fetch → cached offline │
│ ─ Access shared platform services │
└────────────────────────────────────────────────┘
⚙️ 捆绑策略
捆绑在 APK/IPA 中
这些模块打包在本机二进制文件中:
- 主机应用程序→Shell,用于初始化 Re.Pack 联合,管理导航、缓存和身份验证状态。
- Auth App → 处理身份验证、MFA 并存储离线用户令牌或 ID。
- 平台模块→包含其他微应用可访问的所有可重复使用的 SDK、服务、DB 模式、API 和实用程序。
远程(联合)捆绑包
在运行时动态获取并加载:
- 仪表板、MicroApp1、MicroApp2、MicroApp3等。
每个远程包都可以独立进行版本控制和更新——无需完整的应用商店部署。
🔩 使用 Re.Pack 设置模块联合
1. 主机(Shell)配置
// host/webpack.config.js
const { withRepack } = require('@callstack/repack');
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = withRepack({
name: 'host',
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
authApp: 'authApp',
platform: 'platform',
dashboard: 'dashboard',
},
shared: {
react: { singleton: true, eager: true },
'react-native': { singleton: true, eager: true },
},
}),
],
});
2. 远程微应用配置
每个微应用程序都公开其入口点:
// microApp1/webpack.config.js
const { withRepack } = require('@callstack/repack');
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = withRepack({
name: 'microApp1',
exposes: {
'./Entry': './src/Entry',
},
shared: {
react: { singleton: true },
'react-native': { singleton: true },
},
});
🌐 动态联合设置(主机端)
注册联邦解析器
// host/src/federationSetup.js
import { Federated, ScriptManager } from '@callstack/repack/client';
import AsyncStorage from '@react-native-async-storage/async-storage';
import RNFetchBlob from 'rn-fetch-blob';
export async function initFederation() {
// Step 1: Fetch remote configuration from CDN
const remoteConfig = await fetch('https://cdn.example.com/superapp/config.json').then(r => r.json());
// Step 2: Cache remote bundles for offline usage
for (const [name, app] of Object.entries(remoteConfig.microApps)) {
const path = `${RNFetchBlob.fs.dirs.DocumentDir}/${name}.bundle`;
const exists = await RNFetchBlob.fs.exists(path);
if (!exists) {
const data = await fetch(app.url).then(r => r.text());
await RNFetchBlob.fs.writeFile(path, data, 'utf8');
}
// Save local mapping
app.localPath = path;
}
// Step 3: Persist config
await AsyncStorage.setItem('remoteConfig', JSON.stringify(remoteConfig));
// Step 4: Register resolver for both online/offline cases
const resolver = Federated.createURLResolver(name => {
const config = remoteConfig.microApps[name];
return config?.localPath || config?.url;
});
ScriptManager.addResolver(resolver);
}
🧠 Auth App — 联合身份验证和离线登录
联合曝光
// authApp/webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'authApp',
exposes: {
'./AuthModule': './src/AuthModule',
},
shared: ['react', 'react-native'],
}),
],
};
离线感知认证模块
// authApp/src/AuthModule.js
import AsyncStorage from '@react-native-async-storage/async-storage';
export async function loginUser(username, password) {
try {
const token = await authenticateOnline(username, password);
await AsyncStorage.setItem('userToken', token);
return { status: 'online', token };
} catch (e) {
// Fallback to offline mode if token exists
const cachedToken = await AsyncStorage.getItem('userToken');
if (cachedToken) return { status: 'offline', token: cachedToken };
throw new Error('Offline and no cached credentials');
}
}
主机或任何 MicroApp 可以通过联合导入使用 Auth App:
const { loginUser } = await Federated.importModule({
scope: 'authApp',
module: './AuthModule',
});
🧩 平台模块 — 共享服务和 SDK
平台模块充当微应用程序和主机之间的共享层 - 包含可重复使用的部分,例如:
- 数据库(Realm/SQLite)
- 共享钩子和 API 客户端
- 主题、常量和功能标志
- 实用程序 SDK(网络、分析等)
它像任何其他应用程序一样联合,但与主机捆绑以确保可用性。
// platform/src/api/index.js
export const fetchWithAuth = async (url, options = {}) => {
const token = await AsyncStorage.getItem('userToken');
return fetch(url, { ...options, headers: { Authorization: `Bearer ${token}` } });
};
然后在任何微应用中动态导入:
const { fetchWithAuth } = await Federated.importModule({
scope: 'platform',
module: './api/index',
});
📱 加载远程微应用程序(例如仪表板)
// host/src/loadMicroApp.js
import React from 'react';
import { Federated } from '@callstack/repack/client';
export async function loadMicroApp(name) {
const mod = await Federated.importModule({
scope: name,
module: './Entry',
});
return mod.default;
}
// usage
const Dashboard = React.lazy(() => loadMicroApp('dashboard'));
🧭 示例用例
| 用例 | 模块 | 描述 |
|---|---|---|
| 用户登录和离线访问 | 授权应用程序 | 在线验证;在本地缓存令牌以供离线使用 |
| 共享数据库/API | 平台 | 提供共享数据库层和网络 SDK |
| 功能仪表板 | 远程(仪表板) | 动态获取,可独立更新 |
| 离线应用启动 | 主持人 | 使用以下方式预取并加载缓存的 bundlesFederated.createURLResolver |
| 热点更新 | 主机+CDN | 获取更新的远程包并刷新联合配置 |
🔄 离线生命周期摘要
- 在应用程序启动时,主机获取远程配置→缓存包。
- 下次启动时(离线):
-
主机从本地文件系统加载包。
-
授权模块验证缓存的 ID/令牌。
-
微应用程序使用本地捆绑包正常安装。
- 重新连接时:
-
主机同步新配置并更新本地缓存。
✅ 这种架构的优点
- 独立部署: 每个微应用程序都可以独立部署和版本控制。
- 降低发布风险: 无需触及主机二进制文件即可更新微应用程序。
- 离线优先: 所有模块都可以使用缓存的包离线运行。
- 联合身份验证: 跨微应用程序共享的通用登录/令牌逻辑。
- 代码可重用性: 平台模块支持所有应用程序共享 SDK。
- 更好的可扩展性: 多个团队可以在不同的模块上工作而不会发生冲突。
开发者体验
- 独立捆绑包:团队拥有自己的模块→更快的并行开发。
- 轻松入职:开发人员只能在本地运行他们的捆绑包。
- 调试工具:使用 Repack 的开发服务器,我们可以独立检查捆绑包和热重载功能。
经验教训
- 保持基础 shell 应用程序最小化— 仅包含基础内容,没有业务逻辑。
- 必须从一开始就设计离线模式(同步很难改造)。
- 捆绑包版本控制是不可协商的——始终知道用户正在运行哪个版本。
- 最小化跨包依赖关系→将模块视为微前端。
🚀 结论
该架构为React Native带来了 Web 微前端的灵活性,由 Re.Pack 的模块联合提供支持。****
通过结合:
- 用于捆绑管理 + 离线逻辑的主机,
- 用于联合登录和离线身份的Auth 应用程序,
- 共享服务平台模块,
- 以及动态获取的远程微应用程序,
...我们实现了可扩展、模块化的超级应用程序架构——可用于生产、可通过 OTA 更新,并针对离线环境进行了优化。
如果你计划构建一款超级应用,那就从小事做起:正确使用 shell 和 bundle loader,做好离线规划,并保持模块解耦。 其他一切都会水到渠成。作者www.mjsyxx.com