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.xml 和 Info.plist 的自定义值。
1
2
设置工作区
在这个示例中,你不需要 create-expo-module 包含的视图模块。使用以下命令清理默认模块:
- 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
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" } } }
import ExpoModulesCore public class ExpoNativeConfigurationModule: Module { public func definition() -> ModuleDefinition { Name("ExpoNativeConfiguration") Function("getApiKey") { () -> String in "api-key" } } }
import { NativeModule, requireNativeModule } from 'expo'; declare class ExpoNativeConfigurationModule extends NativeModule { getApiKey(): string; } // 此调用从 JSI 加载原生模块对象。 export default requireNativeModule<ExpoNativeConfigurationModule>('ExpoNativeConfiguration');
import ExpoNativeConfigurationModule from './ExpoNativeConfigurationModule'; export function getApiKey(): string { return ExpoNativeConfigurationModule.getApiKey(); }
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> ); }
{ %%placeholder-start%%... %%placeholder-end%% "dependencies": { "expo-native-configuration": "file:.." %%placeholder-start%%... %%placeholder-end%% } }
3
运行示例项目
在项目根目录中,运行 TypeScript 编译器以监听变化并重新构建模块的 JavaScript:
# 在项目根目录运行此命令以启动 TypeScript 编译器- npm run build在另一个终端窗口中,编译并运行示例应用:
# 进入示例项目- 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/config的getConfig方法读取配置时,会调用plugins。相反,mods只会在npx expo prebuild的“同步”阶段被调用。
虽然不是必需的,但建议使用
expo-module-scripts来简化插件开发。它为 TypeScript 和 Jest 提供了推荐的默认配置。更多信息请参阅配置插件指南。
先使用这个最小样板创建你的插件。创建一个用于用 TypeScript 编写插件的 plugin 目录,并在项目根目录添加一个 app.plugin.js 文件,它将作为插件入口点。
创建 plugin/tsconfig.json 文件
{ "extends": "expo-module-scripts/tsconfig.plugin", "compilerOptions": { "outDir": "build", "rootDir": "src" }, "include": ["./src"], "exclude": ["**/__mocks__/*", "**/__tests__/*"] }
为你的插件创建 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 文件
// 此文件为你的插件配置入口文件。 module.exports = require('./plugin/build');
在项目根目录运行 npm run build plugin,以监听模式启动 TypeScript 编译器。接下来,通过在 example/app.json 文件中添加以下一行来配置示例项目使用你的插件:
{ "expo": { %%placeholder-start%%... %%placeholder-end%% "plugins": ["../app.plugin.js"] } }
当你在 example 目录中运行 npx expo prebuild 命令时,终端会通过控制台语句输出“my custom plugin”。
- cd example- npx expo prebuild --clean要将自定义 API key 注入到 AndroidManifest.xml 和 Info.plist 中,请使用 expo/config-plugins 提供的辅助工具 mods。它们可以轻松修改原生文件。在这个示例中,使用 withAndroidManifest 和 withInfoPlist。
顾名思义,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; };
你可以将所有内容合并到一个函数中,从而创建一个自定义插件:
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 字段:
{ "expo": { %%placeholder-start%%... %%placeholder-end%% "plugins": [["../app.plugin.js", { "apiKey": "custom_secret_api" }]] } }
完成此更改后,通过在 example 目录中运行 npx expo prebuild --clean 来测试插件是否正常工作。该命令会执行你的插件并更新原生文件,将 "MY_CUSTOM_API_KEY" 注入到 AndroidManifest.xml 和 Info.plist 中。你可以检查 example/android/app/src/main/AndroidManifest.xml 和 example/ios/exponativeconfigurationexample/Info.plist 的内容来验证这一点。
5
从模块中读取原生值
现在,通过使用平台特定的方法访问其内容,让你的原生模块读取添加到 AndroidManifest.xml 和 Info.plist 中的字段。
在 Android 上,可以使用 packageManager 类从 AndroidManifest.xml 文件中访问 metadata 信息。要读取 "MY_CUSTOM_API_KEY" 的值,请更新 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 文件:
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
下一步
恭喜你,你已经创建了一个与 Android 和 iOS 的 Expo 模块交互的配置插件!
如果你想挑战一下自己,让这个插件更灵活,这个练习对你开放。修改插件,使其允许传入任意一组配置键和值,并添加从模块中读取任意键的功能。
使用 Kotlin 和 Swift 创建原生模块的参考文档。
了解如何为 macOS 和 tvOS 平台添加支持。