使用 Re.Pack 和 Module Federation 构建可扩展的 React Native 超级应用程序

375 阅读7分钟

使用 React Native 构建超级应用:经验教训

过去几年,超级应用已成为企业整合多种服务的一种流行方式。超级应用不会强迫用户下载和维护多个应用,而是提供了一个单一入口,让多个小应用或功能模块可以共存。

当我的团队开始使用React Native 构建企业超级应用程序时,我们的目标很简单:

  • 使其具有可扩展性,以便可以轻松插入新模块。
  • 确保它即使在离线环境中(如仓库或远程站点)也能正常工作。
  • 保持开发人员的顺畅体验,以便团队可以并行工作。

这个博客分享了我们的历程、我们面临的挑战以及我们为使一切顺利进行而做出的架构选择。

为什么使用 React Native 打造超级应用?

有很多选择,但 React Native 对我们来说脱颖而出,因为:

  • 跨平台支持:适用于 iOS 和 Android 的一个代码库。
  • 丰富的生态系统:成熟的导航、状态管理和网络库。
  • OTA(无线)更新:无需等待应用商店批准即可推送更新。
  • 社区和工具:强大的开发者社区和像 Repack 这样的模块化捆绑解决方案。

离线模式:预取和启动策略

离线模式对我们来说至关重要。以下是我们的应对措施:

  1. 预取 bundles
  • 在线时,应用程序会下载所需的包并将其缓存在本地。
  1. 捆绑配置存储
  • 有关哪些捆绑包可用及其版本的元数据存储在本地。
  1. 离线启动流程
  • 启动时,如果设备处于离线状态,应用程序会检查本地捆绑配置。
  • 弹出窗口通知用户他们处于离线模式。
  • 要求用户提供其EMP ID以继续使用缓存功能。
  1. 重新连接时同步
  • 一旦设备恢复在线,就会下载更新的软件包并替换旧的软件包。

这确保用户可以继续无缝工作而不会出现连接问题。

在本文中,我们将深入探讨如何使用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 和实用程序。

远程(联合)捆绑包

在运行时动态获取并加载:

  • 仪表板MicroApp1MicroApp2MicroApp3等。

每个远程包都可以独立进行版本控制和更新——无需完整的应用商店部署。


🔩 使用 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获取更新的远程包并刷新联合配置

🔄 离线生命周期摘要

  1. 在应用程序启动时,主机获取远程配置→缓存包。
  2. 下次启动时(离线):
  • 主机从本地文件系统加载包。

  • 授权模块验证缓存的 ID/令牌。

  • 微应用程序使用本地捆绑包正常安装。

    1. 重新连接时:
  • 主机同步新配置并更新本地缓存。


✅ 这种架构的优点

  • 独立部署: 每个微应用程序都可以独立部署和版本控制。
  • 降低发布风险: 无需触及主机二进制文件即可更新微应用程序。
  • 离线优先: 所有模块都可以使用缓存的包离线运行。
  • 联合身份验证: 跨微应用程序共享的通用登录/令牌逻辑。
  • 代码可重用性: 平台模块支持所有应用程序共享 SDK。
  • 更好的可扩展性: 多个团队可以在不同的模块上工作而不会发生冲突。

开发者体验

  • 独立捆绑包:团队拥有自己的模块→更快的并行开发。
  • 轻松入职:开发人员只能在本地运行他们的捆绑包。
  • 调试工具:使用 Repack 的开发服务器,我们可以独立检查捆绑包和热重载功能。

经验教训

  • 保持基础 shell 应用程序最小化— 仅包含基础内容,没有业务逻辑。
  • 必须从一开始就设计离线模式(同步很难改造)。
  • 捆绑包版本控制是不可协商的——始终知道用户正在运行哪个版本。
  • 最小化跨包依赖关系→将模块视为微前端。

🚀 结论

该架构为React Native带来了 Web 微前端的灵活性,由 Re.Pack 的模块联合提供支持。****

通过结合:

  • 用于捆绑管理 + 离线逻辑的主机
  • 用于联合登录和离线身份的Auth 应用程序
  • 共享服务平台模块,
  • 以及动态获取的远程微应用程序,

...我们实现了可扩展、模块化的超级应用程序架构——可用于生产、可通过 OTA 更新,并针对离线环境进行了优化。

如果你计划构建一款超级应用,那就从小事做起:正确使用 shell 和 bundle loader,做好离线规划,并保持模块解耦。 其他一切都会水到渠成。作者www.mjsyxx.com