持续的原生生成 (CNG)
了解如何使用持续原生生成 (CNG) 和预构建来管理你的原生项目。
单个原生项目本身的维护、扩展和更新都很复杂。在跨平台应用中,你有多个原生项目,你必须维护这些项目并保持最新的操作系统版本,以避免在任何第三方依赖中落后太多。
¥A single native project on its own is complicated to maintain, scale, and update. In a cross-platform app, you have multiple native projects that you must maintain and keep up to date with the latest operating system releases to avoid falling too far behind in any third-party dependencies.
随着你的原生项目的增长,来自第三方依赖的复杂性也会增加,从而使升级变得复杂并减缓开发者的开发速度。这不鼓励添加高级原生功能,并导致应用功能减弱。在跨平台应用中,这种复杂性在每个平台上都会成倍增加。
¥As your native project grows, the complexity from third-party dependencies increases, complicating upgrades and slowing down developer momentum. This discourages adding advanced native functionality and leads to less powerful apps. In cross-platform apps, this complexity is multiplied across each platform.
为了解决这个问题,我们引入了持续原生生成的概念。不是一次性创建原生项目并在代码库的整个生命周期内维护对这些原生项目的自定义,而是仅在需要时(例如调试或构建时)生成短暂的原生项目。这些项目是从标准模板加上配置或定义如何自定义模板的自定义代码生成的。结果是一个原生项目,可以编译成原生应用,并根据开发者的需要进行任何自定义。但是,开发者只负责维护其自定义的定义,而不是所有原生项目代码。
¥To address this, we've introduced the concept of Continuous Native Generation. Instead of creating native projects a single time and maintaining customizations to those native projects for the lifetime of the codebase, short-lived native projects are generated only when needed, such as when debugging or building. These projects are generated from a standard template plus configuration or custom code that defines how the template should be customized. The result is a native project that can be compiled into a native app with any customizations desired by the developer. However, the developer is responsible for only maintaining the definition of their customizations, rather than all of the native project code.
React Native 应用中的 CNG
¥CNG in React Native apps
React Native 应用可以使用 预建 使用 CNG 来自动升级、安装或卸载库、应用白标自定义、跨多个应用共享配置、减少 孤立代码 等等。
¥React Native apps can use CNG by using Prebuild to automate upgrades, install or uninstall libraries, apply white label customizations, share configuration across multiple apps, reduce orphaned code, and more.
Expo 作为一个框架,通过结合以下工具来启用 CNG:
¥Expo as a framework enables CNG by combining the following tools:
-
应用配置 文件。
¥The app config file.
-
传递给
npx expo prebuild
命令的参数。¥Arguments passed to the
npx expo prebuild
command. -
项目中安装的
expo
版本以及相应的 预构建模板。¥Version of
expo
that's installed in the project and corresponding prebuild template. -
自动链接,用于链接 package.json 中找到的 原生模块。
¥Autolinking, for linking native modules found in the package.json.
-
原生订阅者,用于减少入口点文件中的原生代码副作用,例如 MainApplication 或 AppDelegate。
¥Native subscribers, for reducing native code side-effects in entry point files, such as MainApplication or AppDelegate.
-
用于代码签名附加目标和权利的 EAS 凭证。
¥EAS Credentials for code signing additional targets and entitlements.
最终结果是一个工作流程,开发者可以使用应用配置表达任何原生应用并通过运行 npx expo prebuild
连续生成该项目。
¥The end result is a workflow where a developer can express any native application with the app config and generate that project continuously — by running npx expo prebuild
.
用法
¥Usage
可以通过运行以下命令来使用预构建:
¥Prebuild can be used by running:
-
npx expo prebuild
这将创建用于运行 React 代码的 android 和 ios 目录。如果你手动修改生成的目录,那么你下次运行 npx expo prebuild --clean
时可能会丢失所做的更改。相反,使用 配置插件 ,它们是在预构建期间对原生项目执行修改的函数。
¥This creates the android and ios directories for running your React code. If you modify the generated directories manually then you risk losing your changes the next time you run npx expo prebuild --clean
. Instead, use config plugins , which are functions that perform modifications on native projects during prebuild.
我们强烈建议你出于 常见问题 部分中列出的原因使用 Prebuild,但系统是 完全可选,你可以随时停止使用它。
¥We highly recommend using Prebuild for the reasons listed in the common questions section, but the system is fully optional, and you can stop using it at any time.
与 EAS 构建一起使用
¥Usage with EAS Build
如果你的项目不包含 android 和 ios 目录,EAS Build 将在编译前运行 Prebuild 生成这些原生目录。这是使用 npx create-expo-app
创建的任何项目的默认行为。
¥If your project does not contain android and ios directories, EAS Build will run Prebuild to generate these native directories before compilation. This is the default behavior for any project created using npx create-expo-app
.
对于具有 android 和 ios 目录的项目,EAS Build 将不会运行 Prebuild,以避免覆盖你对原生目录所做的任何更改。
¥For a project that has android and ios directories, EAS Build will not run Prebuild to avoid overwriting any changes you've made to the native directories.
如果你通过 本地编译(运行 npx expo prebuild
、或 npx expo run:android
或 npx expo run:ios
)对应用进行故障排除,你仍然可以使用 Prebuild 和 EAS Build 在构建过程中生成新的原生目录。在这种情况下,将 android 和 ios 目录添加到 .gitignore 或 .easignore 文件中:
¥If you troubleshoot your app by compiling it locally (running npx expo prebuild
, or npx expo run:android
or npx expo run:ios
), you can still use Prebuild with EAS Build to generate fresh native directories during the build process. In this scenario, add the android and ios directories to .gitignore or .easignore files:
+ /android
+ /ios
与 Expo CLI 运行命令一起使用
¥Usage with Expo CLI run commands
你可以通过运行以下命令在本地执行原生构建:
¥You can perform a native build locally by running:
# Build your native Android project
-
npx expo run:android
# Build your native iOS project
-
npx expo run:ios
如果原生目录不存在,npx expo prebuild
将针对特定平台运行一次。在随后使用这些 run
命令时,请手动运行 npx expo prebuild --clean
以确保原生代码与你的本地配置最新同步。
¥If native directories are absent, npx expo prebuild
will run once for the specific platform. On subsequent uses of these run
commands, manually run npx expo prebuild --clean
to ensure the native code is freshly synchronized with your local configuration.
平台支持
¥Platform support
Prebuild 目前支持 Android 和 iOS。不需要 Web 支持,因为没有为 Web 和它在 Web 浏览器中运行的 Web 应用生成的原生项目。使用 --platform
选项为各个平台运行预构建:
¥Prebuild currently supports Android and iOS. Web support is not required because there is no native project to generate for the web and the web app it runs in a web browser. Use the --platform
option to run prebuild for individual platforms:
-
npx expo prebuild --platform ios
依赖
¥Dependencies
Prebuild 首先从与每个 Expo SDK 版本相对应的模板初始化新的原生项目。这也与特定的 React 和 React Native 版本一致。如果项目的 React 和 React Native 版本与 模板的 package.json 的 dependencies
字段中指定的预期版本不同,则在运行 npx expo prebuild
时你将看到警告。
¥Prebuild begins by initializing new native projects from a template corresponding to each Expo SDK version. This also aligns with specific React and React Native versions. You will see a warning when running npx expo prebuild
when your project's React and React Native versions differ from the expected versions from specified in the dependencies
field of the template's package.json if they differ.
你可以使用 --skip-dependency-update
选项跳过更改 npm 包版本:
¥You can skip changing npm package versions with the --skip-dependency-update
option:
-
npx expo prebuild --skip-dependency-update react-native,react
包管理器
¥Package managers
当 dependencies 发生更改时,Prebuild 将使用项目当前使用的包管理器重新安装库(这是从锁定文件中推断出来的)。你可以通过提供以下之一来强制使用特定的包管理器:--npm
、--yarn
、--pnpm
。
¥When the dependencies are changed, Prebuild will reinstall libraries using the package manager that is currently used in the project (this is inferred from the lockfile). You can force a specific package manager by providing one of: --npm
, --yarn
, --pnpm
.
通过传递 --no-install
命令可以跳过所有安装,这对于快速测试生成很有用。
¥All installations can be skipped by passing the --no-install
command, which is useful for testing generation quickly.
清理
¥Clean
--clean
选项在生成之前删除任何现有的原生目录。在没有 --clean
选项的情况下重新运行 npx expo prebuild
将在现有文件之上分层更改,这样速度更快,但在某些情况下可能不会产生相同的结果。
¥The --clean
option deletes any existing native directories before generating. Re-running npx expo prebuild
without the --clean
option will layer changes on top of the existing files, which is faster, but may not produce the same results in some cases.
例如,一些配置插件不是幂等的。当项目利用多个 "危险修饰语" 将正则表达式更改添加到应用代码时,可能会导致意外行为。这就是为什么使用 --clean
选项是使用预构建命令的最安全方式,并且通常在大多数情况下都推荐这样做。
¥For example, some config plugins aren't idempotent. When a project utilizes multiple "dangerous modifiers" to add regex changes to an app's code, it can lead to unexpected behavior. This is why using the --clean
option is the safest way to use the prebuild command and is generally recommended in most cases.
使用 --clean
选项
¥Using --clean
option
使用 --clean
选项时,如果你对 git 代码存储库有任何未提交的更改,你将收到警告,因为此选项将删除并重新创建所有原生项目文件。该提示是可选的,在 CI 中遇到时会跳过。你可以通过启用环境变量 EXPO_NO_GIT_STATUS=1
来禁用此检查。
¥When using the --clean
option, you'll be warned if you have any uncommitted changes to your git code repository, as this option will delete and recreate all of your native project files. This prompt is optional and will be skipped when encountered in CI. You can disable this check by enabling the environment variable EXPO_NO_GIT_STATUS=1
.
在某些情况下,开发者可能希望经常在工作流程之间进行交换。例如,你可能希望在 Android Studio 和 Xcode 中原生构建自定义功能,然后将该功能移至本地配置插件中。
¥There are cases where developers may want to swap between workflows often. For example, you may want to build custom functionality natively in Android Studio and Xcode, and then move that functionality into local config plugins.
模板
¥Templates
你可以自定义 配置插件 生成原生目录的方式。许多配置插件已经存在,可以进行大量修改,社区库也经常提供自己的插件。你可以 查看一些流行插件的列表 了解更多信息。
¥You can customize how the native directories are generated by config plugins. Many config plugins already exist for lots of modifications, and community libraries often ship their own as well. You can see a list of some popular plugins for more information.
Prebuild 从模板文件开始,然后使用配置插件进行修改。模板文件基于 Expo SDK 版本,来自 npm 包 expo-template-bare-minimum
。你可以通过将 --template /path/to/template.tgz
传递给 npx expo prebuild
命令来更改使用的模板。通常不建议这样做,因为 @expo/prebuild-config
中的基本修饰符对模板文件做出了一些未记录的假设,因此维护自定义模板可能很棘手。
¥Prebuild starts from template files, which are then modified with config plugins. The template files are based on the Expo SDK version and come from the npm package expo-template-bare-minimum
. You can change the template used by passing --template /path/to/template.tgz
to the npx expo prebuild
command. This is not generally recommended because the base modifiers in @expo/prebuild-config
make some undocumented assumptions about the template files, so it may be tricky to maintain your custom template.
注意:在所有包都是从私有注册表下载并且 npm 公共注册表访问被阻止的网络环境中,必须将本地可用的模板传递给预构建命令。了解有关使用默认模板的本地版本的详细信息。
¥Note: In network environments where all packages are downloaded from a private registry and npm public registry access is blocked, a locally-available template must be passed to the prebuild command. Learn more about using a local version of the default template.
副作用
¥Side effects
除了生成 android 和 ios 目录之外,npx expo prebuild
还执行一些副作用。消除这些副作用的工作正在进行中 - 理想情况下,运行 npx expo prebuild
将生成 Android 和 iOS 项目,并且项目的其余部分保持不变。
¥npx expo prebuild
performs several side effects outside of generating the android and ios directories. Work is in progress to eliminate these side effects — ideally, running npx expo prebuild
would generate the Android and iOS projects and leave the rest of the project untouched.
除了生成原生目录外,prebuild 还进行了以下修改:
¥In addition to generating the native directories, prebuild also makes the following modifications:
-
修改 package.json 中的
scripts
字段,将expo start --android
和expo start --ios
替换为expo run:android
和expo run:ios
¥Modifies the
scripts
field in the package.json to replaceexpo start --android
andexpo start --ios
withexpo run:android
andexpo run:ios
-
修改 package.json 中的
dependencies
字段¥Modifies the
dependencies
field in the package.json
对 scripts
字段的方便更改是改变开发者在预构建之前/之后处理应用的方式的唯一副作用。所有其他更改都可以保留并提交给 git,以最大限度地减少运行预构建时的差异。
¥The convenience change to the scripts
field is the only side effect that alters how a developer works on their app before/after prebuild. All other changes can be left in place and committed to git to minimize the diff when running prebuild.
可选
¥Optionality
Prebuild 是可选的,可与所有 Expo 工具和服务无缝协作。对于现有的 React Native 项目,其中原生项目是手动管理的,请勿使用 npx expo prebuild
,因为这可能会覆盖任何手动自定义。开发者可以在采用其他 Expo 工具和工作流程的同时继续制作 (直接更改其原生项目)。稍后,他们可以将手动自定义移动到应用配置和/或配置插件,然后采用 CNG。
¥Prebuild is optional and works seamlessly with all Expo tools and services. For existing React Native projects, where the native projects are managed manually, do not use npx expo prebuild
, as that may overwrite any manual customizations. Developers can continue to make (direct changes to their native projects) while adopting other Expo tools and workflows. Later on, they can move their manual customizations to app config and/or config plugins, and then adopt CNG.
Expo 提供的所有内容,包括 EAS、Expo CLI 和 Expo SDK 中的库,都是为完全支持裸 React Native 项目而构建的,因为这是使用 npx expo prebuild
支持项目的最低要求。唯一的例外是 Expo 应用,它只能在包含 Expo Go 运行时中不存在的原生代码的 JavaScript 回退时加载任意 React Native 项目。
¥Everything offered by Expo including EAS, Expo CLI, and the libraries in the Expo SDK are built to fully support bare React Native projects as this is a minimum requirement for supporting projects using npx expo prebuild
. The only exception is the Expo Go app, which can load arbitrary React Native projects only if they include JavaScript fallbacks for native code absent in the Expo Go runtime.
常见问题
¥Common questions
CNG
How does CNG helps with project upgrades?
React Native developers who don't use Continuous Native Generation have reported that upgrading their apps to the latest version of React Native is the number one weakness of the library as per React Native Survey (2022).
When using CNG, the upgrade process simply involves upgrading the npm dependencies, app config, and re-running npx expo prebuild --clean
.
Is CNG limited to React Native projects?
No, CNG is a versatile pattern that can be applied to any native project. While Expo Prebuild is a tool that implements CNG specifically for React Native projects, the concept itself is not limited to this framework.
How does community uses CNG?
Here are a few community examples of difficult native features converted into simple configuration files, which have allowed developers to build more powerful apps without compromising on iteration speed:
-
iOS Safari Extensions: Here, the process of creating a Safari Extension for iOS, which is a notoriously difficult feature to implement, is reduced to a few of lines of JSON.
-
iMessage Sticker App: This Expo config plugin can generate an entire iMessage Sticker App from a JSON object.
-
Cross-platform end-to-end testing: Configure native apps to support E2E testing with Detox in a single-line.
-
The entire Firebase suite: Here you can see the entire native Firebase suite going from a multi-step native configuration process across multiple IDEs, down to basic JSON configuration.
-
Cross-platform home screen widgets: This Expo config plugin can generate a home screen widget for Android and iOS.
-
Notification extension and code signing: This Expo config plugin generates a notification extension target on iOS and it augments the EAS credentials service to keep zero-config code signing working.
-
Apple App Clips: This Expo config plugin takes the process of generating an Apple App Clip from a multi-step process, ranging across multiple targets, and reduces it to a single line
["react-native-app-clip", { "name": "My App Clip" }]
.
At any point, these features can be easily added and removed, without any side effects. CNG allows developers to experiment with complex features and iterate on them quickly without worrying about the long-term maintenance costs or potential orphaned code in their project.
Can CNG be used for operating systems other than Android and iOS?
Absolutely! CNG is an abstract concept that can be applied to any operating system. Although Expo Prebuild officially implements CNG for Android and iOS, it also provides abstract platform support for developers to create implementations for additional platforms.
Is using Expo a requirement for CNG?
Not at all. CNG is an open pattern that can be adopted by any community. We've defined the pattern abstractly to help other communities understand how they can adopt CNG for their own projects.
How does CNG compare to web development patterns such as Static Site Generation (SSG)?
CNG shares similarities with SSG in that it generates a project from a set of inputs. However, CNG differs from SSG in its output. It generates native runtime code instead of static website code. This means the native project is generated on-demand, and the generated source code and configuration are discarded once the native project is compiled into a native app.
Is it possible to use CNG with an existing brownfield project?
CNG is designed to manage the entire state of a native project continuously. As a result, it's not intended for use with existing brownfield projects. However, you can use CNG to generate a new native project, which can then be integrated into an existing brownfield project.
Prebuild
Expo Prebuild streamlines CNG processing. Here are some issues in the React Native development cycle that are addressed by Prebuild:
How can Prebuild help with sensible project upgrades?
Building native code requires familiarity with the platform's tooling, creating a steep learning curve. This challenge intensifies in cross-platform development due to multiple platforms. Cross-platform tooling doesn't help if you must implement many features in platform-specific native code.
When bootstrapping a native app, there's initial code and configuration that you may not understand. Yet you are not responsible for maintaining it. Eventually, you'll need to understand this code to upgrade your app safely. This challenge often leads developers to upgrade incorrectly or start a new app, copying existing source code.
With Prebuild, upgrading is much closer to upgrading a pure JavaScript application. Bump the versions in your package.json, regenerate the native project, and you should be ready to continue development.
How does Prebuild simplify cross-platform configuration?
Cross-platform configurations such as the app icon, name, splash screen, and so on must be implemented manually in native code. These implementations are often quite different for each platform.
With Prebuild cross-platform configurations are handled at the config plugin level, and the developer only needs to set a single value like "icon": "./icon.png"
to have all icon generation taken care of.
How can I manage dependency side-effects with Prebuild?
Many complex native packages require additional setup beyond installing and autolinking. For example, a camera library requires permission settings to be added AndroidManifest.xml for Android and Info.plist for iOS. This additional setup can be considered a configuration side effect of a package. Pasting the required side effect code into your project's native files can lead to difficult native compilation errors, and it's also code that you now own and maintain.
With Prebuild library authors, who know how to configure their library better than anyone, can create a testable and versioned script called a config plugin, to automate adding the required configuration side effects for their library. This means library side effects can be more expressive, powerful, and stable. For native code side effects, we also provide Android Lifecycle Listeners and AppDelegate Subscribers which come standard in the default prebuild template.
How does Prebuild help with orphaned code?
When you uninstall a package, you have to be certain you removed all of the side effects required to make that package work. If you miss anything, it leads to orphaned code that you cannot trace back to any particular package, this code builds up and makes your project harder to understand and maintain.
With Prebuild the only side effect is the config plugin in a project's Expo config (app.json), which will throw an error when the corresponding node module has been uninstalled, meaning a lot less orphaned configuration.
When Prebuild might not be the right fit for a project
Here are some reasons Expo Prebuild might not be the right fit for a particular project:
Platform compatibility
Prebuild can only be used for native platforms that are supported by the Expo SDK. This means Android and iOS for the time being. Except for web, which doesn't require npx expo prebuild
since it uses the browser instead of a custom native runtime.
Making changes directly is quicker than modularizing and automating
All native changes must be added with native modules (using React Native's built-in Native Module APIs or the Expo Modules API) and config plugins. This means if you want to quickly add a native file to your project to experiment, then you may be better off running prebuild and adding the file manually, then working your way back into the system with a monorepo. We plan to speed this process up by adding functionality to Expo Autolinking that finds native project files outside of the native directories and links them before building.
If you want to modify the configuration, such as the gradle.properties file, you'll have to write a plugin (example). This can be easily automated with helper plugin libraries, however, it is a bit slower if you need to do it often.
Config plugin support in the community
Not all packages support Expo Prebuild yet. If you find a library that requires extra setup after installation and doesn't yet have a config plugin, we recommend opening a pull request or an issue so that the maintainer is aware of the feature request.
Many packages, such as react-native-blurhash
, don't require any additional native configuration beyond what is handled by autolinking and so no config plugin is required.
Other packages, such as react-native-ble-plx
, do require additional setup and therefore require a config plugin to be used with npx expo prebuild
(in this case there's an external plugin called @config-plugins/react-native-ble-plx
).
Alternatively, we also have a repo for out-of-tree config plugins which provides plugins for popular packages that haven't adopted the system yet. Think of this like DefinitelyTyped for TypeScript. We prefer packages ship their own config plugin, but if they haven't adopted the system yet, the community can use the packages listed in the repo.