This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.

教程:创建带有配置插件的模块

编辑页面

使用 Expo Modules API 创建原生模块和配置插件的教程。


配置插件 让你可以自定义在 持续原生生成(CNG) 项目中使用 npx expo prebuild 生成的原生 Android 和 iOS 项目。你可以用它们向原生配置文件添加属性、将资源复制到原生项目,或者应用更高级的配置,例如添加一个 应用扩展目标

作为应用开发者,配置插件可以帮助你应用默认 应用配置 中未暴露的自定义设置。作为库作者,它们可以让你为使用你库的开发者自动配置原生项目。

本教程将说明如何从零开始创建一个新的配置插件,以及如何从 Expo 模块中读取插件注入到 AndroidManifest.xmlInfo.plist 的自定义值。

1

初始化模块

先使用 create-expo-module 初始化一个新的 Expo 模块项目。这会为 Android、iOS 和 TypeScript 搭建脚手架,并包含一个示例项目,方便在应用中测试该模块。运行以下命令开始:

Terminal
npx create-expo-module expo-native-configuration

本指南使用 expo-native-configuration/ExpoNativeConfiguration 作为模块项目名称。不过,你可以选择任何你喜欢的名称。

2

设置工作区

在这个示例中,你不需要 create-expo-module 包含的视图模块。使用以下命令清理默认模块:

Terminal
cd expo-native-configuration
rm android/src/main/java/expo/modules/nativeconfiguration/ExpoNativeConfigurationView.kt
rm ios/ExpoNativeConfigurationView.swift
rm src/ExpoNativeConfigurationView.tsx src/ExpoNativeConfiguration.types.ts
rm src/ExpoNativeConfigurationView.web.tsx src/ExpoNativeConfigurationModule.web.ts

找到以下文件并用提供的最小样板替换它们:

  • android/src/main/java/expo/modules/nativeconfiguration/ExpoNativeConfigurationModule.kt
  • ios/ExpoNativeConfigurationModule.swift
  • src/ExpoNativeConfigurationModule.ts
  • src/index.ts
  • example/App.tsx
  • package.json
android/src/main/java/expo/modules/nativeconfiguration/ExpoNativeConfigurationModule.kt
package expo.modules.nativeconfiguration import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition class ExpoNativeConfigurationModule : Module() { override fun definition() = ModuleDefinition { Name("ExpoNativeConfiguration") Function("getApiKey") { return@Function "api-key" } } }
ios/ExpoNativeConfigurationModule.swift
import ExpoModulesCore public class ExpoNativeConfigurationModule: Module { public func definition() -> ModuleDefinition { Name("ExpoNativeConfiguration") Function("getApiKey") { () -> String in "api-key" } } }
src/ExpoNativeConfigurationModule.ts
import { NativeModule, requireNativeModule } from 'expo'; declare class ExpoNativeConfigurationModule extends NativeModule { getApiKey(): string; } // 此调用从 JSI 加载原生模块对象。 export default requireNativeModule<ExpoNativeConfigurationModule>('ExpoNativeConfiguration');
src/index.ts
import ExpoNativeConfigurationModule from './ExpoNativeConfigurationModule'; export function getApiKey(): string { return ExpoNativeConfigurationModule.getApiKey(); }
example/App.tsx
import * as ExpoNativeConfiguration from 'expo-native-configuration'; import { Text, View } from 'react-native'; export default function App() { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>API key: {ExpoNativeConfiguration.getApiKey()}</Text> </View> ); }
package.json
{ %%placeholder-start%%... %%placeholder-end%% "dependencies": { "expo-native-configuration": "file:.." %%placeholder-start%%... %%placeholder-end%% } }

3

运行示例项目

在项目根目录中,运行 TypeScript 编译器以监听变化并重新构建模块的 JavaScript:

Terminal
# 在项目根目录运行此命令以启动 TypeScript 编译器
npm run build

在另一个终端窗口中,编译并运行示例应用:

Terminal
# 进入示例项目
cd example

# 重新安装依赖
rm -rf node_modules && npm install

# 在 Android 上运行示例应用
npx expo run:android

# 在 iOS 上运行示例应用
npx expo run:ios

你应该会看到一个包含文本“API key: api-key”的屏幕。

4

创建新的配置插件

插件 是同步函数,接收一个 ExpoConfig 并返回一个修改后的 ExpoConfig。按照惯例,这些函数以 with 开头。将你的插件命名为 withMyApiKey,或者使用其他名称,只要符合这一惯例即可。

下面是一个基础配置插件函数示例:

const withMyApiKey = config => { return config; };

你也可以使用 mods,它们是用于修改原生项目中文件(例如源代码或 plist、xml 配置文件)的异步函数。mods 对象与应用配置的其他部分不同,因为它在初次读取后不会序列化。这使你可以在_代码生成期间_执行操作。

编写配置插件时,请遵循以下注意事项:

  • 插件必须是同步的,并且其返回值必须可序列化,除了添加的任何 mods 之外。
  • expo/configgetConfig 方法读取配置时,会调用 plugins。相反,mods 只会在 npx expo prebuild 的“同步”阶段被调用。

先使用这个最小样板创建你的插件。创建一个用于用 TypeScript 编写插件的 plugin 目录,并在项目根目录添加一个 app.plugin.js 文件,它将作为插件入口点。

创建 plugin/tsconfig.json 文件

plugin/tsconfig.json
{ "extends": "expo-module-scripts/tsconfig.plugin", "compilerOptions": { "outDir": "build", "rootDir": "src" }, "include": ["./src"], "exclude": ["**/__mocks__/*", "**/__tests__/*"] }

为你的插件创建 plugin/src/index.ts 文件

plugin/src/index.ts
import { ConfigPlugin } from 'expo/config-plugins'; const withMyApiKey: ConfigPlugin = config => { console.log('my custom plugin'); return config; }; export default withMyApiKey;

在根目录创建 app.plugin.js 文件

app.plugin.js
// 此文件为你的插件配置入口文件。 module.exports = require('./plugin/build');

在项目根目录运行 npm run build plugin,以监听模式启动 TypeScript 编译器。接下来,通过在 example/app.json 文件中添加以下一行来配置示例项目使用你的插件:

example/app.json
{ "expo": { %%placeholder-start%%... %%placeholder-end%% "plugins": ["../app.plugin.js"] } }

当你在 example 目录中运行 npx expo prebuild 命令时,终端会通过控制台语句输出“my custom plugin”。

Terminal
cd example
npx expo prebuild --clean

要将自定义 API key 注入到 AndroidManifest.xmlInfo.plist 中,请使用 expo/config-plugins 提供的辅助工具 mods。它们可以轻松修改原生文件。在这个示例中,使用 withAndroidManifestwithInfoPlist

顾名思义,withAndroidManifest 允许你读取并修改 AndroidManifest.xml 文件。使用 AndroidConfig 辅助工具向主应用添加一个 metadata 项,如下所示:

const withMyApiKey: ConfigPlugin<{ apiKey: string }> = (config, { apiKey }) => { config = withAndroidManifest(config, config => { const mainApplication = AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults); AndroidConfig.Manifest.addMetaDataItemToMainApplication( mainApplication, 'MY_CUSTOM_API_KEY', apiKey ); return config; }); return config; };

同样,你可以使用 withInfoPlist 来修改 Info.plist 的值。使用 modResults 属性,你可以添加自定义值,如下代码片段所示:

const withMyApiKey: ConfigPlugin<{ apiKey: string }> = (config, { apiKey }) => { config = withInfoPlist(config, config => { config.modResults['MY_CUSTOM_API_KEY'] = apiKey; return config; }); return config; };

你可以将所有内容合并到一个函数中,从而创建一个自定义插件:

plugin/src/index.ts
import { withInfoPlist, withAndroidManifest, AndroidConfig, ConfigPlugin, } from 'expo/config-plugins'; const withMyApiKey: ConfigPlugin<{ apiKey: string }> = (config, { apiKey }) => { config = withInfoPlist(config, config => { config.modResults['MY_CUSTOM_API_KEY'] = apiKey; return config; }); config = withAndroidManifest(config, config => { const mainApplication = AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults); AndroidConfig.Manifest.addMetaDataItemToMainApplication( mainApplication, 'MY_CUSTOM_API_KEY', apiKey ); return config; }); return config; }; export default withMyApiKey;

插件准备好后,更新示例应用,将你的 API key 作为配置选项传递给插件。如下所示修改 example/app.json 中的 plugins 字段:

example/app.json
{ "expo": { %%placeholder-start%%... %%placeholder-end%% "plugins": [["../app.plugin.js", { "apiKey": "custom_secret_api" }]] } }

完成此更改后,通过在 example 目录中运行 npx expo prebuild --clean 来测试插件是否正常工作。该命令会执行你的插件并更新原生文件,将 "MY_CUSTOM_API_KEY" 注入到 AndroidManifest.xmlInfo.plist 中。你可以检查 example/android/app/src/main/AndroidManifest.xmlexample/ios/exponativeconfigurationexample/Info.plist 的内容来验证这一点。

5

从模块中读取原生值

现在,通过使用平台特定的方法访问其内容,让你的原生模块读取添加到 AndroidManifest.xmlInfo.plist 中的字段。

在 Android 上,可以使用 packageManager 类从 AndroidManifest.xml 文件中访问 metadata 信息。要读取 "MY_CUSTOM_API_KEY" 的值,请更新 android/src/main/java/expo/modules/nativeconfiguration/ExpoNativeConfigurationModule.kt 文件:

android/src/main/java/expo/modules/nativeconfiguration/ExpoNativeConfigurationModule.kt
package expo.modules.nativeconfiguration import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition import android.content.pm.PackageManager class ExpoNativeConfigurationModule() : Module() { override fun definition() = ModuleDefinition { Name("ExpoNativeConfiguration") Function("getApiKey") { val applicationInfo = appContext?.reactContext?.packageManager?.getApplicationInfo(appContext?.reactContext?.packageName.toString(), PackageManager.GET_META_DATA) return@Function applicationInfo?.metaData?.getString("MY_CUSTOM_API_KEY") } } }

在 iOS 上,你可以使用 Bundle.main.object(forInfoDictionaryKey: "") 方法读取 Info.plist 属性的内容。要访问之前添加的 "MY_CUSTOM_API_KEY" 值,请按如下所示更新 ios/ExpoNativeConfigurationModule.swift 文件:

ios/ExpoNativeConfigurationModule.swift
import ExpoModulesCore public class ExpoNativeConfigurationModule: Module { public func definition() -> ModuleDefinition { Name("ExpoNativeConfiguration") Function("getApiKey") { return Bundle.main.object(forInfoDictionaryKey: "MY_CUSTOM_API_KEY") as? String } } }

6

运行你的模块

当你的原生模块读取了添加到原生文件中的字段后,你现在就可以运行示例应用,并使用 ExamplePlugin.getApiKey() 函数访问你的自定义 API key。

Terminal
cd example

# 执行你的插件并更新原生文件
npx expo prebuild

# 在 Android 上运行示例应用
npx expo run:android

# 在 iOS 上运行示例应用
npx expo run:ios

下一步

恭喜你,你已经创建了一个与 Android 和 iOS 的 Expo 模块交互的配置插件!

如果你想挑战一下自己,让这个插件更灵活,这个练习对你开放。修改插件,使其允许传入任意一组配置键和值,并添加从模块中读取任意键的功能。

Expo 模块 API 参考

使用 Kotlin 和 Swift 创建原生模块的参考文档。

额外的平台支持

了解如何为 macOS 和 tvOS 平台添加支持。