教程:创建具有配置插件的模块
编辑
一篇关于使用 Expo Modules API 创建本地模块和配置插件的教程。
配置插件 让您可以自定义通过 npx expo prebuild 生成的本地 Android 和 iOS 项目,适用于 持续本地生成 (CNG) 项目。您可以利用它们向本地配置文件添加属性,复制资产到本地项目,或应用高级配置,例如添加 应用扩展目标。
作为应用开发者,配置插件帮助您应用在默认的 应用配置 中未公开的自定义设置。作为库作者,它们使您能够为使用您库的开发者自动配置本地项目。
本教程解释了如何从零开始创建一个新的配置插件,并读取您插件注入到 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; } // This call loads the native module object from the 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。 plugins在每次expo/config的getConfig方法读取配置时被调用。相比之下,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 密钥注入到 AndroidManifest.xml 和 Info.plist 中,请使用 expo/config-plugins 提供的帮助器 mods。这些帮助器使修改本地文件变得简单。对于这个示例,使用 withAndroidManifest 和 withInfoPlist。
如其名称所示,withAndroidManifest 允许您读取和修改 AndroidManifest.xml 文件。使用 AndroidConfig 帮助器将元数据项添加到主应用程序,如下所示:
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 密钥作为配置选项传递给插件。将 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 文件中的元数据。要读取 "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 平台的支持。