Expo Go 是一个预编译的通用客户端 App,内置了 Expo SDK 支持的原生模块集合。任何不在这个集合中的原生模块(如 react-native-mmkv 的 NitroModules、自定义 TurboModules)都无法在 Expo Go 中运行——因为 Expo Go 的二进制文件中没有编译这些模块的原生代码。
原理
原生模块(无论是旧 Bridge 架构的 Native Modules、新架构的 TurboModules、还是 Nitro Modules)都需要在编译期将 C++/ObjC/Kotlin 代码链接到 App 二进制中。Expo Go 的二进制是 Expo 团队预先编译好的,只包含 Expo SDK 白名单中的模块。
你的 JS 代码是运行时加载的(可以随时更新),但原生模块是编译期固定的(必须重新构建 App)。
Development Build 解决方案
Development Build 是为你的项目定制编译的开发客户端:
npx expo run:ios # 本地构建
eas build --profile development # EAS 云构建
它会把 package.json 中所有需要原生代码的依赖编译进去,包括 MMKV、Reanimated 等。
MindGym 的 fallback 策略
M1 阶段使用 Expo Go 开发,MMKV import 时崩溃。解决方案是 store/persist.ts 中用 try/catch require() 降级到内存 Map:
let storage: MmkvLike
try {
const { createMMKV } = require('react-native-mmkv')
storage = createMMKV({ id: 'mindgym-storage' })
} catch {
// Expo Go 下 NitroModules 不可用,降级为内存存储
const map = new Map<string, string>()
storage = { getString: (k) => map.get(k), set: (k, v) => map.set(k, v), ... }
}
代价:Expo Go 下数据不持久化(杀 App 即丢失),M8 切到 Development Build 后移除 fallback。
如何判断一个库是否需要 Development Build
- 查看库的
package.json是否有ios/或android/目录(原生代码) - 查看是否依赖
react-native-nitro-modules或有*.podspec/build.gradle - Expo 兼容性目录 标注了每个库是否支持 Expo Go
参见
- Expo vs 裸 RN — Expo 工作流概览
- MMKV 与 JSI — MMKV 的 JSI 原理
参考
- Expo: Development Builds
- MindGym
store/persist.ts— fallback 实现