# React Native 多 Bundle 系统集成指南

本指南将帮助你将多 Bundle 系统集成到你的 React Native 项目中。

## 目录

- [安装](#安装)
- [Metro 配置](#metro-配置)
- [Native 模块集成](#native-模块集成)
  - [Android](#android)
  - [iOS](#ios)
- [初始化多 Bundle 系统](#初始化多-bundle-系统)
- [配置模块](#配置模块)
- [构建多 Bundle](#构建多-bundle)
- [常见问题](#常见问题)

## 安装

```bash
npm install @bm-fe/react-native-multi-bundle
# 或
yarn add @bm-fe/react-native-multi-bundle
```

## Metro 配置

### 1. 复制 Metro 配置模板

将 `templates/metro.config.js.template` 复制到你的项目根目录，重命名为 `metro.config.js`。

### 2. 调整配置

根据你的项目结构，修改以下配置：

```javascript
// 模块路径规则（glob 模式）
const modulePaths = ['src/modules/**'];

// 共享依赖路径（这些代码会被打包到主 bundle）
const sharedDependencies = [
  'node_modules/@bm-fe/react-native-multi-bundle/**',
  'src/navigation/**'
];
```

### 3. 环境变量（可选）

你也可以通过环境变量覆盖配置：

```bash
RN_MULTI_BUNDLE_MODULE_PATHS='["src/modules/**"]' \
RN_MULTI_BUNDLE_SHARED_DEPS='["src/multi-bundle/**","src/navigation/**"]' \
npx react-native start
```

## Native 模块集成

### Android（自动链接，无需手动配置）

#### 自动链接工作原理

安装包后，React Native CLI 会自动：
1. 读取包中的 `react-native.config.js` 配置
2. 将 `ModuleLoaderPackage` 添加到 `PackageList` 中
3. 在构建时自动集成原生模块

#### 验证自动链接

安装包后，可以通过以下命令验证自动链接是否生效：

```bash
npx react-native config
```

在输出中应该能看到 `@bm-fe/react-native-multi-bundle` 的配置信息。

#### 确保 assets 目录存在

确保 `android/app/src/main/assets/` 目录存在，bundle 文件将放在这里：

```bash
mkdir -p android/app/src/main/assets/modules
```

#### 手动集成（可选，仅在自动链接不工作时使用）

如果自动链接不工作（极少数情况），可以手动集成：

<details>
<summary>点击展开手动集成步骤</summary>

##### 1. 复制 Native 模块文件

将以下文件从 `node_modules/@bm-fe/react-native-multi-bundle/templates/native/android/` 复制到你的 Android 项目：

- `ModuleLoaderModule.kt` → `android/app/src/main/java/com/yourpackage/ModuleLoaderModule.kt`
- `ModuleLoaderPackage.kt` → `android/app/src/main/java/com/yourpackage/ModuleLoaderPackage.kt`

**重要**：修改文件中的包名 `com.yourpackage` 为你的实际包名。

##### 2. 注册 ModuleLoaderPackage

在 `MainApplication.kt` 中添加：

```kotlin
import com.yourpackage.ModuleLoaderPackage

class MainApplication : Application(), ReactApplication {
  override val reactHost: ReactHost by lazy {
    getDefaultReactHost(
      context = applicationContext,
      packageList = PackageList(this).packages.apply {
        add(ModuleLoaderPackage())  // 手动添加
      },
    )
  }
  // ...
}
```

</details>

### iOS（自动链接，无需手动配置）

#### 自动链接工作原理

安装包后，React Native CLI 和 CocoaPods 会自动：
1. 读取包中的 `react-native-multi-bundle.podspec`
2. 将 `ModuleLoader` 原生模块添加到项目中
3. 在构建时自动集成

#### 安装依赖

安装 npm 包后，运行 pod install：

```bash
cd ios && pod install
```

#### 验证自动链接

安装包后，可以通过以下命令验证自动链接是否生效：

```bash
npx react-native config
```

在输出中应该能看到 `@bm-fe/react-native-multi-bundle` 的 iOS 配置信息。

#### 准备 Bundles 目录

确保 iOS 项目中有 `Bundles` 目录用于存放 bundle 文件：

1. 在 Xcode 中，右键点击项目 → New Group，创建 `Bundles` 目录
2. 或者在终端中创建：
   ```bash
   mkdir -p ios/YourProject/Bundles/modules
   ```

3. **重要**：确保 `Bundles` 目录已添加到 Xcode 项目的 Resources 中：
   - 在 Xcode 中，右键点击 `Bundles` 目录 → Add Files to "YourProject"
   - 确保 "Create folder references" 已选中（蓝色文件夹图标）
   - 这样 bundle 文件才会被打包到 app 中

#### Bundle 文件结构

构建完成后，iOS bundle 文件应放置在：

```
ios/YourProject/Bundles/
├── bundle-manifest.json
├── main.jsbundle
└── modules/
    ├── home.jsbundle
    ├── details.jsbundle
    └── settings.jsbundle
```

#### 手动集成（可选，仅在自动链接不工作时使用）

<details>
<summary>点击展开手动集成步骤</summary>

##### 1. 复制 Native 模块文件

将以下文件从 `node_modules/@bm-fe/react-native-multi-bundle/ios/ModuleLoader/` 复制到你的 iOS 项目：

- `ModuleLoaderModule.h`
- `ModuleLoaderModule.mm`

在 Xcode 中：
1. 右键点击项目 → Add Files to "YourProject"
2. 选择这两个文件
3. 确保 "Copy items if needed" 已勾选

##### 2. 验证编译

iOS 端的模块会自动注册（通过 `RCT_EXPORT_MODULE`），无需额外配置。

重新构建项目：

```bash
npx react-native run-ios
```

</details>

## 初始化多 Bundle 系统

在应用入口（通常是 `App.tsx`）中初始化：

```typescript
import React, { useEffect, useState } from 'react';
import { initMultiBundle, LocalBundleManager } from '@bm-fe/react-native-multi-bundle';
import { Platform } from 'react-native';

function App() {
  const [ready, setReady] = useState(false);

  useEffect(() => {
    async function bootstrap() {
      const result = await initMultiBundle({
        // 模块路径规则（glob 模式）
        modulePaths: ['src/modules/**'],
        
        // 共享依赖路径
        sharedDependencies: [
          'node_modules/@bm-fe/react-native-multi-bundle/**',
          'src/navigation/**'
        ],
        
        // Manifest 提供者（可选，默认使用 LocalBundleManager）
        manifestProvider: async () => {
          return await LocalBundleManager.getCurrentBundleManifest();
        },
        
        // 预加载模块列表（可选）
        preloadModules: ['settings'],
        
        // 开发服务器配置（可选）
        devServer: {
          host: Platform.OS === 'android' ? '10.0.2.2' : 'localhost',
          port: 8081,
        },
        
        // 错误处理回调（可选）
        onError: (error) => {
          console.error('MultiBundle error:', error);
        },
      });

      if (result.success) {
        setReady(true);
      }
    }

    bootstrap();
  }, []);

  if (!ready) {
    return <LoadingScreen />;
  }

  // ... 其余代码
}
```

## 配置模块

### 1. 创建 multi-bundle.config.json

将 `templates/multi-bundle.config.json.template` 复制到项目根目录，重命名为 `multi-bundle.config.json`。

### 2. 配置模块

编辑 `multi-bundle.config.json`：

```json
{
  "formatVersion": 1,
  "platforms": ["ios", "android"],
  "main": {
    "entry": "src/main-entry.tsx",
    "version": "1.0.0"
  },
  "modules": [
    {
      "id": "home",
      "entry": "src/modules/Home/index.ts",
      "version": "1.0.0",
      "dependencies": [],
      "lazy": true
    }
  ]
}
```

### 3. 创建模块入口文件

每个模块需要有一个入口文件，例如 `src/modules/Home/index.ts`：

```typescript
import HomeScreen from './HomeScreen';

const exports = {
  routes: {
    Home: HomeScreen,
  },
  components: {
    HomeScreen,
  },
};

// 注册模块
const ModuleRegistry = globalThis.__ModuleRegistry;
if (ModuleRegistry) {
  ModuleRegistry.registerModule('home', exports);
}

export default exports;
```

## 构建多 Bundle

### 使用 npm 脚本

在 `package.json` 中添加：

```json
{
  "scripts": {
    "bundle:ios": "node node_modules/@bm-fe/react-native-multi-bundle/scripts/build-multi-bundle.js ios",
    "bundle:android": "node node_modules/@bm-fe/react-native-multi-bundle/scripts/build-multi-bundle.js android",
    "bundle:ios:prod": "node node_modules/@bm-fe/react-native-multi-bundle/scripts/build-multi-bundle.js ios --env production",
    "bundle:android:prod": "node node_modules/@bm-fe/react-native-multi-bundle/scripts/build-multi-bundle.js android --env production"
  }
}
```

### 使用 CLI 命令

如果全局安装了包：

```bash
multi-bundle-build ios
multi-bundle-build android --env production
```

### 构建产物

构建完成后，产物位于 `build/bundles/<platform>/` 目录：

- 主 bundle：`index.android.bundle` (Android) 或 `main.jsbundle` (iOS)
- 模块 bundle：`modules/<moduleId>.bundle` (Android) 或 `modules/<moduleId>.jsbundle` (iOS)
- Manifest：`bundle-manifest.json`

## 使用模块路由

### 方式一：使用 createModuleRouteLoader（推荐）

```typescript
import { createModuleRouteLoader } from '@bm-fe/react-native-multi-bundle';

// 创建路由加载器
export const createHomeScreen = createModuleRouteLoader('home', 'Home');

// 在 React Navigation 中使用
<Stack.Screen name="Home" getComponent={createHomeScreen} />
```

**路由 Props 传递说明：**

`createModuleRouteLoader` 会自动将 React Navigation 的路由 props 传递给模块组件，包括：

- `navigation`：导航对象，用于页面跳转
- `route`：路由对象，包含路由参数和配置

模块组件可以直接使用这些 props：

```typescript
// 在模块组件中（如 HomeScreen.tsx）
import React from 'react';
import { View, Text } from 'react-native';
import { NativeStackScreenProps } from '@react-navigation/native-stack';

type Props = NativeStackScreenProps<RootStackParamList, 'Home'>;

const HomeScreen = ({ navigation, route }: Props) => {
  // 可以使用 navigation 进行页面跳转
  const handleNavigate = () => {
    navigation.navigate('Details', { id: '123' });
  };

  // 可以使用 route.params 获取路由参数
  const params = route.params;

  return (
    <View>
      <Text>Home Screen</Text>
      {/* ... */}
    </View>
  );
};

export default HomeScreen;
```

如果需要传递额外的 props，可以通过 React Navigation 的 `initialParams` 或 `getInitialProps`：

```typescript
<Stack.Screen 
  name="Home" 
  getComponent={createHomeScreen}
  initialParams={{ customProp: 'value' }}
/>
```

### 方式二：使用路由注册系统

```typescript
import { registerModuleRoute, getModuleRoute } from '@bm-fe/react-native-multi-bundle';

// 注册路由
registerModuleRoute('home', 'Home');

// 获取路由组件
const HomeScreen = getModuleRoute('home', 'Home');
```

## 常见问题

### 1. 模块加载失败

- 检查 manifest 是否正确配置
- 确认模块 ID 与 manifest 中的 ID 一致
- 查看控制台错误信息

### 2. Metro 构建问题

- 确认 `metro.config.js` 配置正确
- 检查模块路径规则是否匹配实际路径
- 清除 Metro 缓存：`npx react-native start --reset-cache`

### 3. Native 模块未找到

- 确认已正确注册 ModuleLoaderPackage
- 检查包名是否正确
- 重新构建原生应用

### 4. 开发环境问题

- 确认开发服务器正在运行
- 检查 `devServer` 配置是否正确
- 验证 manifest URL 是否可访问

### 5. 配置文件找不到

如果执行打包命令时提示 `Config file not found`：

- **确认配置文件位置**：`multi-bundle.config.json` 应该在项目根目录（与 `package.json` 同级）
- **确认执行目录**：打包命令应该在项目根目录执行，或者通过 npm scripts 执行
- **手动指定项目根目录**：如果仍有问题，可以通过环境变量指定：
  ```bash
  PROJECT_ROOT=/path/to/your/project node node_modules/@bm-fe/react-native-multi-bundle/scripts/build-multi-bundle.js ios
  ```
- **检查文件路径**：确认错误信息中的路径是否正确指向你的项目根目录

## 更多信息

- [API 参考](./src/multi-bundle/README.md)
- [示例应用](./examples/demo-app/README.md)
- [模块开发规范](./docs/模块%20Bundle%20开发规范.md)
