前言

目前跨平台方面,除了Flutter 就是 React Native(RN) ,我们今天来玩转一下 RN。

前期准备

先准备 node 环境:

brew install node

可以看到如下输出:

==> Auto-updating Homebrew...
Adjust how often this is run with `$HOMEBREW_AUTO_UPDATE_SECS` or disable with
`$HOMEBREW_NO_AUTO_UPDATE=1`. Hide these hints with `$HOMEBREW_NO_ENV_HINTS=1` (see `man brew`).
==> Auto-updated Homebrew!
Updated 2 taps (homebrew/core and homebrew/cask).
==> New Formulae
aklomp-base64: Fast Base64 stream encoder/decoder in C99, with SIMD acceleration
asm-lsp: Language server for NASM/GAS/GO Assembly
bulletty: Pretty feed reader (ATOM/RSS) that stores articles in Markdown files
claude-code-router: Tool to route Claude Code requests to different models and customize any request
claude-code-templates: CLI tool for configuring and monitoring Claude Code
container: Create and run Linux containers using lightweight virtual machines
container-canary: Test and validate container requirements against versioned manifests
corepack: Package acting as bridge between Node projects and their package managers
cpptrace: Simple, portable, and self-contained stacktrace library for C++11 and newer
docmd: Minimal Markdown documentation generator
doh: Stand-alone DNS-over-HTTPS resolver using libcurl
eigen@3: C++ template library for linear algebra
fake-gcs-server: Emulator for Google Cloud Storage API
filebrowser: Web File Browser
framework-tool-tui: TUI for controlling and monitoring Framework Computers hardware
gcl: GNU Common Lisp
getparty: Multi-part HTTP download manager
gitlab-ci-linter: Command-line tool to lint GitLab CI YAML files
gocheat: TUI Cheatsheet for keybindings, hotkeys and more
goclone: Website Cloner
hack-browser-data: Command-line tool for decrypting and exporting browser data
haraka: Fast, highly extensible, and event driven SMTP server
hexhog: Hex viewer/editor
intelli-shell: Like IntelliSense, but for shells
kibi: Text editor in ≤1024 lines of code, written in Rust
ktea: Kafka TUI client
magika: Fast and accurate AI powered file content types detection
mark: Sync your markdown files with Confluence pages
mcat: Terminal image, video, directory, and Markdown viewer
mysql-to-sqlite3: Transfer data from MySQL to SQLite
node@24: Open-source, cross-platform JavaScript runtime environment
openlist: New AList fork addressing anti-trust issues
openssl@1.1: Cryptography and SSL/TLS Toolkit
permify: Open-source authorization service & policy engine based on Google Zanzibar
postgres-language-server: Language Server for Postgres
qcoro6: C++ Coroutines for Qt
quint: Core tool for the Quint specification language
rails-mcp-server: MCP server for Rails applications
reddix: Reddit, refined for the terminal
rumdl: Markdown Linter and Formatter written in Rust
sqlite3-to-mysql: Transfer data from SQLite to MySQL
supabase: Open source Firebase alternative
taskline: Tasks, boards & notes for the command-line habitat
teamtype: Peer-to-peer, editor-agnostic collaborative editing of local text files
ts_query_ls: LSP implementation for Tree-sitter's query files
tscriptify: Golang struct to TypeScript class/interface converter
tuios: Terminal UI OS (Terminal Multiplexer)

You have 3 outdated formulae installed.

==> Fetching downloads for: node
==> Downloading https://ghcr.io/v2/homebrew/core/node/manifests/25.1.0_1
########################################################################## 100.0%
==> Fetching dependencies for node: brotli, c-ares, icu4c@77, libnghttp2, libnghttp3, libngtcp2, libuv, simdjson, sqlite, uvwasi, lz4, xz and zstd
==> Downloading https://ghcr.io/v2/homebrew/core/brotli/manifests/1.2.0
########################################################################## 100.0%
==> Fetching brotli
==> Downloading https://ghcr.io/v2/homebrew/core/brotli/blobs/sha256:c9d272f3ac33
########################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/c-ares/manifests/1.34.5-1
########################################################################## 100.0%
==> Fetching c-ares
==> Downloading https://ghcr.io/v2/homebrew/core/c-ares/blobs/sha256:c152939c8cbf
########################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/icu4c/77/manifests/77.1
########################################################################## 100.0%
==> Fetching icu4c@77
==> Downloading https://ghcr.io/v2/homebrew/core/icu4c/77/blobs/sha256:54d609febe
########################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/libnghttp2/manifests/1.68.0
########################################################################## 100.0%
==> Fetching libnghttp2
==> Downloading https://ghcr.io/v2/homebrew/core/libnghttp2/blobs/sha256:b848ec2c
########################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/libnghttp3/manifests/1.12.0
########################################################################## 100.0%
==> Fetching libnghttp3
==> Downloading https://ghcr.io/v2/homebrew/core/libnghttp3/blobs/sha256:81311931
########################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/libngtcp2/manifests/1.17.0
########################################################################## 100.0%
==> Fetching libngtcp2
==> Downloading https://ghcr.io/v2/homebrew/core/libngtcp2/blobs/sha256:279e17de0
########################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/libuv/manifests/1.51.0-1
########################################################################## 100.0%
==> Fetching libuv
==> Downloading https://ghcr.io/v2/homebrew/core/libuv/blobs/sha256:89296bb1520f6
########################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/simdjson/manifests/4.2.1
########################################################################## 100.0%
==> Fetching simdjson
==> Downloading https://ghcr.io/v2/homebrew/core/simdjson/blobs/sha256:c1499dc97a
########################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/sqlite/manifests/3.51.0
########################################################################## 100.0%
==> Fetching sqlite
==> Downloading https://ghcr.io/v2/homebrew/core/sqlite/blobs/sha256:643e8196600c
########################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/uvwasi/manifests/0.0.23-1
########################################################################## 100.0%
==> Fetching uvwasi
==> Downloading https://ghcr.io/v2/homebrew/core/uvwasi/blobs/sha256:74534d557b06
########################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/lz4/manifests/1.10.0-1
########################################################################## 100.0%
==> Fetching lz4
==> Downloading https://ghcr.io/v2/homebrew/core/lz4/blobs/sha256:c30d8dad4decfc0
########################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/xz/manifests/5.8.1
########################################################################## 100.0%
==> Fetching xz
==> Downloading https://ghcr.io/v2/homebrew/core/xz/blobs/sha256:ce65e4c93e377bef
########################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/zstd/manifests/1.5.7-1
########################################################################## 100.0%
==> Fetching zstd
==> Downloading https://ghcr.io/v2/homebrew/core/zstd/blobs/sha256:873feb2d747bb2
########################################################################## 100.0%
==> Fetching node
==> Downloading https://ghcr.io/v2/homebrew/core/node/blobs/sha256:bc3d4077b6fa78
########################################################################## 100.0%
==> Installing dependencies for node: brotli, c-ares, icu4c@77, libnghttp2, libnghttp3, libngtcp2, libuv, simdjson, sqlite, uvwasi, lz4, xz and zstd
==> Installing node dependency: brotli
==> Downloading https://ghcr.io/v2/homebrew/core/brotli/manifests/1.2.0
Already downloaded: /Users/mac/Library/Caches/Homebrew/downloads/43040be3ccb15626fbea32e35b07134720b9790ad66caf27be03edac1dfbfeb2--brotli-1.2.0.bottle_manifest.json
==> Pouring brotli--1.2.0.sonoma.bottle.tar.gz
  /usr/local/Cellar/brotli/1.2.0: 33 files, 1.8MB
==> Installing node dependency: c-ares
==> Downloading https://ghcr.io/v2/homebrew/core/c-ares/manifests/1.34.5-1
Already downloaded: /Users/mac/Library/Caches/Homebrew/downloads/c52b1c91460667312755a235f78ac192a45bff39befbba6da9aa72a17263c559--c-ares-1.34.5-1.bottle_manifest.json
==> Pouring c-ares--1.34.5.sonoma.bottle.1.tar.gz
  /usr/local/Cellar/c-ares/1.34.5: 176 files, 945.3KB
==> Installing node dependency: icu4c@77
==> Downloading https://ghcr.io/v2/homebrew/core/icu4c/77/manifests/77.1
Already downloaded: /Users/mac/Library/Caches/Homebrew/downloads/35ec2c3adb94255ba47424a4334da9616d97e1ec6d59ed907b368535018bed0a--icu4c@77-77.1.bottle_manifest.json
==> Pouring icu4c@77--77.1.sonoma.bottle.tar.gz
  /usr/local/Cellar/icu4c@77/77.1: 277 files, 80MB
==> Installing node dependency: libnghttp2
==> Downloading https://ghcr.io/v2/homebrew/core/libnghttp2/manifests/1.68.0
Already downloaded: /Users/mac/Library/Caches/Homebrew/downloads/16e7ebe81106c5b0b363cf16b9a25118a344811cd317e5042943682b17675a76--libnghttp2-1.68.0.bottle_manifest.json
==> Pouring libnghttp2--1.68.0.sonoma.bottle.tar.gz
 /usr/local/Cellar/libnghttp2/1.68.0: 14 files, 735.9KB
==> Installing node dependency: libnghttp3
==> Downloading https://ghcr.io/v2/homebrew/core/libnghttp3/manifests/1.12.0
Already downloaded: /Users/mac/Library/Caches/Homebrew/downloads/870c7405e7536d3964d6a4063f552f6ba7db08a1057381685a9f9c177df71c9f--libnghttp3-1.12.0.bottle_manifest.json
==> Pouring libnghttp3--1.12.0.sonoma.bottle.tar.gz
 /usr/local/Cellar/libnghttp3/1.12.0: 20 files, 554.3KB
==> Installing node dependency: libngtcp2
==> Downloading https://ghcr.io/v2/homebrew/core/libngtcp2/manifests/1.17.0
Already downloaded: /Users/mac/Library/Caches/Homebrew/downloads/f9ac96017064e1c9c23c32050e1fedb487cd3f6a744fc6c8bfd3a3ee8fa90605--libngtcp2-1.17.0.bottle_manifest.json
==> Pouring libngtcp2--1.17.0.sonoma.bottle.tar.gz
  /usr/local/Cellar/libngtcp2/1.17.0: 21 files, 1.2MB
==> Installing node dependency: libuv
==> Downloading https://ghcr.io/v2/homebrew/core/libuv/manifests/1.51.0-1
Already downloaded: /Users/mac/Library/Caches/Homebrew/downloads/3134253b57ab9327739c97027f8b90160da109e06dda0dc4d2d222a6df0ab9b8--libuv-1.51.0-1.bottle_manifest.json
==> Pouring libuv--1.51.0.sonoma.bottle.1.tar.gz
  /usr/local/Cellar/libuv/1.51.0: 34 files, 1.2MB
==> Installing node dependency: simdjson
==> Downloading https://ghcr.io/v2/homebrew/core/simdjson/manifests/4.2.1
Already downloaded: /Users/mac/Library/Caches/Homebrew/downloads/346ea6428cf23cfe5c9cf9aa74ba48ce2cf6858f68b5287036d6b8bb09ab76ef--simdjson-4.2.1.bottle_manifest.json
==> Pouring simdjson--4.2.1.sonoma.bottle.tar.gz
  /usr/local/Cellar/simdjson/4.2.1: 17 files, 6.4MB
==> Installing node dependency: sqlite
==> Downloading https://ghcr.io/v2/homebrew/core/sqlite/manifests/3.51.0
Already downloaded: /Users/mac/Library/Caches/Homebrew/downloads/8465785061e36b7a1fc885b00fc23949c696b13d8b9569ea685ae294b6f57fd4--sqlite-3.51.0.bottle_manifest.json
==> Pouring sqlite--3.51.0.tahoe.bottle.tar.gz
  /usr/local/Cellar/sqlite/3.51.0: 13 files, 4.9MB
==> Installing node dependency: uvwasi
==> Downloading https://ghcr.io/v2/homebrew/core/uvwasi/manifests/0.0.23-1
Already downloaded: /Users/mac/Library/Caches/Homebrew/downloads/d9fc39e8e8eb3c4e33b14bafb8f46ce7c30a8814a9d2ff809f4a27525ec96893--uvwasi-0.0.23-1.bottle_manifest.json
==> Pouring uvwasi--0.0.23.sonoma.bottle.1.tar.gz
  /usr/local/Cellar/uvwasi/0.0.23: 15 files, 257.0KB
==> Installing node dependency: lz4
==> Downloading https://ghcr.io/v2/homebrew/core/lz4/manifests/1.10.0-1
Already downloaded: /Users/mac/Library/Caches/Homebrew/downloads/8e11e90eb21a06e0f199af9d80e011e3693c77dd353b2477579d95c8471a5802--lz4-1.10.0-1.bottle_manifest.json
==> Pouring lz4--1.10.0.tahoe.bottle.1.tar.gz
 /usr/local/Cellar/lz4/1.10.0: 24 files, 696.2KB
==> Installing node dependency: xz
==> Downloading https://ghcr.io/v2/homebrew/core/xz/manifests/5.8.1
Already downloaded: /Users/mac/Library/Caches/Homebrew/downloads/86a115cc1d43ff8a480fd907f812e70a403e1675d8a7223f61bbb08cbd2adc27--xz-5.8.1.bottle_manifest.json
==> Pouring xz--5.8.1.tahoe.bottle.tar.gz
  /usr/local/Cellar/xz/5.8.1: 96 files, 2.3MB
==> Installing node dependency: zstd
==> Downloading https://ghcr.io/v2/homebrew/core/zstd/manifests/1.5.7-1
Already downloaded: /Users/mac/Library/Caches/Homebrew/downloads/14f45413747fef87558cd195b1104e55d9739f9574f146051dbd6569ded74a94--zstd-1.5.7-1.bottle_manifest.json
==> Pouring zstd--1.5.7.tahoe.bottle.1.tar.gz
  /usr/local/Cellar/zstd/1.5.7: 32 files, 2.4MB
==> Installing node
==> Pouring node--25.1.0_1.sonoma.bottle.tar.gz
 /usr/local/Cellar/node/25.1.0_1: 2,269 files, 78.4MB
==> Running `brew cleanup node`...
Disable this behaviour by setting `HOMEBREW_NO_INSTALL_CLEANUP=1`.
Hide these hints with `HOMEBREW_NO_ENV_HINTS=1` (see `man brew`).
==> Caveats
zsh completions have been installed to:
  /usr/local/share/zsh/site-functions

开始

运行 expo 工程:

npx create-expo-app@latest

expo 工程是现在 rn 推荐的创建 app 的方式,他的演进经历了如下过程:
演进

输入工程名字,可以看到如下结果:
成功

打开目录,可以看到如下结构:
成功

其中 app 目录是我们的工作目录:
成功

对这个文件的介绍:

  1. _layout.tsx​ 是各级页面的容器或导航控制器,控制着子页面的呈现方式(是堆叠、标签还是模态)。
  2. 其他 .tsx文件就是具体的页面内容,相当于 UIViewController。
  3. index.tsx​ 是一个特殊文件,它代表所在目录的默认页面(路径为 /)。

集成现有项目

更改项目目录

rn 的目录遵循一定的结构,我们最好按他的约定来组织目录:

masterboy git:(master) ✗ tree -I 'node_modules|.git' -L 2 -a
.
├── .gitee
│   ├── ISSUE_TEMPLATE.zh-CN.md
│   └── PULL_REQUEST_TEMPLATE.zh-CN.md
├── .gitignore
├── .vscode
│   └── settings.json
├── .xcode.env
├── .xcode.env.local
├── ios
│   ├── .DS_Store
│   ├── .xcode.env
│   ├── .xcode.env.local
│   ├── bin
│   ├── build
│   ├── build_info
│   ├── cache
│   ├── doc
│   ├── extensions
│   ├── Gemfile
│   ├── Gemfile.lock
│   ├── gems
│   ├── LICENSE
│   ├── MasterBoy
│   ├── MasterBoy.xcodeproj
│   ├── MasterBoy.xcworkspace
│   ├── Podfile
│   ├── Podfile.lock
│   ├── Pods
│   ├── README.en.md
│   ├── README.md
│   └── specifications
├── package-lock.json
└── package.json

参考官方教程:Integration with Existing Apps 即可。

前置工作

下载 package.json 并运行 npm install 安装脚手架。

curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.82-stable/template/package.json
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1171  100  1171    0     0     34      0  0:00:34  0:00:33  0:00:01   311

然后:

masterboy git:(master) ✗ npm install
npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm warn deprecated @humanwhocodes/config-array@0.13.0: Use @eslint/config-array instead
npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated @humanwhocodes/object-schema@2.0.3: Use @eslint/object-schema instead
npm warn deprecated eslint@8.57.1: This version is no longer supported. Please see https://eslint.org/version-support for other options.

added 835 packages, and audited 836 packages in 4m

158 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

管理 ruby 版本

先下载:

masterboy git:(master) curl -O https://raw.githubusercontent.com/react-native-community/template/refs/heads/0.82-stable/template/Gemfile
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   510  100   510    0     0     93      0  0:00:05  0:00:05 --:--:--   109

pod install

再执行:

bundle install

bundle exec pod install

当前项目中集成 React Native

最重要的一步来了,要把 RN 集成到我们项目中。链接 RN 和 我们项目肯定就是修改 Podfile 无疑了。
思路是:我们直接从 expo 中拿一个模板来即可。

在之前的从 0 到 1 生成的 RN 工程中,我们使用了工具链 expo,这次我们仍然使用 expo 让刚才的项目生成 iOS 目录:

npx expo prebuild

执行该命令后,会生成一个 iOS 项目,我们看一下他的 podfile,然后直接拷贝过来,用到我们原先的群管家中:

# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
  'require.resolve(
    "react-native/scripts/react_native_pods.rb",
    {paths: [process.argv[1]]},
  )', __dir__]).strip

platform :ios, min_ios_version_supported
prepare_react_native_project!

target 'MasterBoy' do
  config = use_native_modules!

  # Use static frameworks for React Native compatibility
  use_frameworks! :linkage => :static
  
  use_react_native!(
    :path => config[:reactNativePath],
    # An absolute path to your application root.
    :app_path => "#{Pod::Config.instance.installation_root}/..",
    # Enable Fabric (New Architecture)
    :fabric_enabled => true,
    # Enable Hermes
    :hermes_enabled => true
  )
  
  pod 'SnapKit','5.6.0'
  pod 'YYText','1.0.7'
  pod 'FDFullscreenPopGesture','1.1'
  pod 'Toast-Swift', '5.0.1'
  pod 'Alamofire','5.6.4'
  pod 'SwiftyJSON','5.0.1'
  pod 'MJRefresh', '3.7.5'
  pod 'JLRoutes', '2.1'
  pod 'LGAlertView'
  pod 'NVActivityIndicatorView'
  pod 'SDCycleScrollView', '1.82'
  
  pod 'RxSwift', '6.5.0'
  pod 'RxCocoa', '6.5.0'
  
  # Add the Firebase pod for Google Analytics
  pod 'ReachabilitySwift'
  pod 'Kingfisher', '7.8.0'
  pod 'LookinServer', :subspecs => ['Swift'], :configurations => ['Debug']
  
  pod 'CocoaLumberjack','3.8.1'


  # Pods for MasterBoy
  
  pod 'TZImagePickerController/Basic' # No location code

  pod 'TPNS-iOS','1.4.0.1'
  
  pod 'GBDeviceInfo', '~> 6.0'


  post_install do |installer|
    react_native_post_install(
      installer,
      config[:reactNativePath],
      :mac_catalyst_enabled => false,
      # :ccache_enabled => true
    )
  end

end

增加的部分大概意思就是引入 React Native 库,接着,我们执行 pod install,就发现 pod 工程中多了很多和 RN 相关的库:

rn库文件

运行 RN 项目

我们找一个文件,来写 RN 相关的代码:

//
//  MBDReactNativeViewController.swift
//  MasterBoy
//
//  Created by Claude on 2025/11/13.
//

// 在文件顶部添加
#if compiler(>=5.9)
#warning("RCTRootView is deprecated but still recommended for Brownfield apps in RN 0.82")
#endif

import UIKit
import React

class MBDReactNativeViewController: UIViewController, RCTBridgeDelegate {

    private var bridge: RCTBridge?
    private var rootView: RCTRootView?

    override func viewDidLoad() {
        super.viewDidLoad()

        // 设置导航栏
        self.title = "React Native"
        self.view.backgroundColor = .white

        // 加载 React Native 视图
        loadReactNativeView()
    }

    private func loadReactNativeView() {
        // 创建 bridge(使用新架构)
        bridge = RCTBridge(delegate: self, launchOptions: nil)
        

        // 创建 RCTRootView
        rootView = RCTRootView(
            bridge: bridge!,
            moduleName: "MasterBoyRN",
            initialProperties: nil
        )

        // 设置 rootView 的frame
        if let rootView = rootView {
            rootView.frame = self.view.bounds
            rootView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

            // 添加到视图层级
            self.view.addSubview(rootView)
        }
    }

    // MARK: - RCTBridgeDelegate

    func sourceURL(for bridge: RCTBridge) -> URL? {
        return getBundleURL()
    }

    private func getBundleURL() -> URL? {
        #if DEBUG
        // 开发模式:从 Metro bundler 加载
        // 确保 Metro bundler 正在运行 (npm start)
        return RCTBundleURLProvider.sharedSettings().jsBundleURL(
            forBundleRoot: "index"
        )
        #else
        // 生产模式:从应用 bundle 加载
        // 需要先构建 JavaScript bundle
        return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
        #endif
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        // 隐藏导航栏(可选)
        // self.navigationController?.setNavigationBarHidden(false, animated: animated)
    }
}

运行后,可以发现文件工程跑起来了:

rn库文件

总结

RN 看起来麻烦,其实你只要懂原理,还是很简单的。