首页 / 文章 / 使用 Expo Prebuild 和 CNG 将原生文件夹视为构建输出

使用 Expo Prebuild 和 CNG 将原生文件夹视为构建输出

Continuous Native Generation 如何让 Expo 应用能够使用自定义原生模块、配置插件及 EAS 密钥,而无需提交代码或手动编辑 iOS 和 Android 文件夹。

1103 词

多年来,Expo 上的 React Native 团队一直面临同样的选择困境:一旦项目需要自定义原生模块、背景音频或第三方 SDK,解决办法就是使用 expo eject,这样原本简洁的 JavaScript 项目就会突然拥有完整的 iosandroid 目录。从那以后,团队还必须维护 CocoaPods、编辑 build.gradle 文件,并调试 Xcode 中出现的错误。连续原生生成(CNG)与 Expo Prebuild 则消除了这种权衡。本指南将解释该机制的运作原理、配置插件如何替代手动修改原生代码、如何解决本地 Android 构建失败的问题,以及如何在不提交敏感信息的情况下将其注入到云端构建中。

作为构建产物的原生代码

CNG 基于一个核心原则:原生项目是自动生成的,而非需要手动维护的。

运行 npx expo prebuild 可以从唯一的配置源,即 app.jsonapp.config.js,生成 iosandroid 文件夹。该工具会读取这些配置,应用其中指定的原生代码修改,最终输出完整的、可直接构建的原生项目。

由于这些文件夹随时都可以重新生成,许多团队会将 /ios/android 加入到 .gitignore 文件中。当某个原生依赖出现故障,或是升级 React Native 时,只需丢弃这些文件夹并重新生成即可。这样一来,日常需要关注的问题就从“有人在 Xcode 项目中做了什么修改?”变成了“配置文件中规定了什么?”。

这也确立了该模型的唯一规则:一旦生成了原生文件夹,就不得手动编辑它们。任何手动修改都会在下一次预构建时消失。如果需要更改,必须通过配置来实现。

配置插件取代手动原生编辑

这就引出了一个显而易见的问题:如果生成的文件不可修改,要如何向AndroidManifest.xml添加权限或调整AppDelegate.mm呢?

配置插件就是答案。配置插件是一种在预构建过程中运行的JavaScript函数,能够以可控且可重复的方式修改原生项目。那些对原生功能有较高要求的应用,比如实时语音处理或3D模型渲染,往往依赖需要深度原生调用的库,而这些库通常会自带相应的插件。

无需编辑原生代码,只需在 app.json 中列出插件及其选项即可。下面的示例使用 expo-build-properties 将 Android 的 compileSdkVersion 设置为 34,这一数值通常会出现在 Gradle 文件中:

{
  "expo": {
    "plugins": [
      [
        "expo-build-properties",
        {
          "android": {
            "compileSdkVersion": 34
          }
        }
      ]
    ]
  }
}

在下次预构建时,该插件会将此设置写入生成的 Android 项目中。由于这是通过声明而非手动应用来完成的,因此每次重新生成项目时该设置都会保留,并且会在代码审查中显示出来。

保持本地 Android 构建环境的良好状态

Expo Go 非常适合早期原型设计,但它仅包含随其捆绑的原生模块。一旦需要使用自定义原生代码,就必须通过开发版本进行测试,可使用 npx expo run:androidnpx expo run:ios 命令。对于那些需要在设备上执行大量运算的应用,比如具备 AI 功能的应用来说,这一点尤为重要,因为必须要在真实设备或模拟器上查看实际性能。

Android 工具链以缓存问题频发而著称。常见的情况是:添加了某个依赖后,下一次本地构建就会因为一些难以理解的 Java 或 Gradle 错误而失败。其根源通常是由于缓存了过时的构建结果,而非代码本身存在问题。

首先应尝试清理缓存后重新构建。进入生成的 android 目录,运行 Gradle 的清理任务以丢弃缓存的结果,然后再回到项目根目录重新构建:

cd android
./gradlew clean
cd ..
npx expo run:android

如果仅执行清理操作无效,CNG还提供了更强大的方案:npx expo prebuild --clean会彻底删除并重新生成原生文件夹,由于这些文件夹中的内容并非人工维护,因此这种方式十分安全。将这两种操作纳入日常流程中,可以避免漫长的调试时间。

向EAS云构建提供机密信息

CNG在与EAS(Expo应用程序服务)结合用于云构建时能发挥最大作用。

以错误监控为例,生产环境的应用确实需要该功能,而集成Sentry则意味着要上传源代码映射文件,这需要一个SENTRY_AUTH_TOKEN。传统上,这个令牌必须传递到每个构建环境,同时又不能出现在代码仓库中,管理起来相当麻烦。

使用 CNG 和 EAS 时,只需将 Sentry 的配置插件添加到 app.json 中,而无需提交令牌。相反,可通过 CLI 在 EAS 中将其注册为环境变量,设置其可见性为 secret 且仅限于项目范围,这样它可以在构建过程中使用,但之后不会显示出来。只需运行一次命令,将占位符值替换为实际的令牌即可:

eas env:create --name SENTRY_AUTH_TOKEN --value your_token_here --visibility secret --scope projecteas env:create --name SENTRY_AUTH_TOKEN --value your_token_here --visibility secret --scope project

在云构建过程中,EAS 会先运行预构建步骤来生成原生项目,Sentry 插件则会从环境中读取该密钥,配置原生 SDK 并上传源映射文件。无论是原生文件还是令牌都不会被放入版本控制系统中。eas env:create 命令的具体参数可能会随 CLI 版本的不同而变化,因此如果该命令被拒绝,请查阅最新的 EAS 文档。

CNG 需要特别注意事项的情况

该模型功能强大,但仍有几种情况需要提前规划:

  • 没有插件的库。如果原生 SDK 没有配置插件,你可能需要自己编写一个小型本地插件,而非直接编辑生成的文件。
  • 现有的旧项目。那些经过多年手工编辑原生代码的应用无法简单地删除相关文件夹;进行迁移时需先将每一项自定义设置转换为配置形式。
  • 团队规范。只有当所有人都将 iosandroid 视为可替换的部分时,CNG 才能发挥作用。在 Xcode 中进行的任何快速修改都可能在下次重新生成时被悄悄丢失。

总结

Expo Prebuild 和 CNG 通过让原生层变为可替换组件,使得复杂且具备生产级质量的 React Native 应用更易于处理:

  • app.jsonapp.config.js 中声明原生依赖,然后由预构建工具负责生成其余部分。
  • 对于每一次原生代码的更改,都使用配置插件,以确保其在重新生成后依然有效。
  • 当本地构建出现莫名其妙的问题时,清理 Gradle 缓存,或使用 prebuild --clean 重新生成。
  • SENTRY_AUTH_TOKEN 等令牌保存在 EAS 环境变量中,切勿放入代码仓库。
  • 由于原生项目已简化为构建输出,团队的注意力便能重新集中在 React Native 代码、流畅的界面以及产品功能上。

    相关阅读