如何在Expo的app.config.ts中使用异步函数的返回结果?
解决方案
方案1:预获取值并写入环境变量
在启动Expo前通过脚本异步请求ngrok的隧道地址,将结果写入环境变量,再让app.config.ts同步读取该变量。
- 编写预启动脚本(比如
scripts/fetch-ngrok-url.js):
const fs = require('fs'); const fetch = require('node-fetch'); async function fetchNgrokUrl() { try { const res = await fetch('http://localhost:4040/api/tunnels'); const data = await res.json(); const publicUrl = data.tunnels.find(t => t.proto === 'https')?.public_url; if (publicUrl) { // 将结果写入.env文件 const envContent = `NGROK_PUBLIC_URL=${publicUrl}\n`; fs.writeFileSync('.env', envContent, { flag: 'a' }); // 追加模式避免覆盖其他变量 console.log('Ngrok URL已写入环境变量'); } else { console.warn('未找到可用的Ngrok HTTPS隧道'); } } catch (err) { console.error('获取Ngrok URL失败:', err); } } fetchNgrokUrl();
- 修改
package.json中的启动命令,先执行脚本再启动Expo:
{ "scripts": { "start": "node scripts/fetch-ngrok-url.js && expo start", "android": "node scripts/fetch-ngrok-url.js && expo start --android", "ios": "node scripts/fetch-ngrok-url.js && expo start --ios" } }
- 在
app.config.ts中读取环境变量:
import { ExpoConfig, ConfigContext } from '@expo/config'; export default ({ config }: ConfigContext): ExpoConfig => { return { ...config, extra: { ...config.extra, apiUrl: process.env.NGROK_PUBLIC_URL || '默认生产地址', }, }; };
方案2:使用Expo Config Plugin处理异步逻辑
Expo的Config Plugin支持异步操作,可以编写自定义插件在配置阶段获取Ngrok地址并注入到app配置中。
- 编写自定义插件(比如
plugins/withNgrokUrl.ts):
import { ConfigPlugin } from '@expo/config-plugins'; import fetch from 'node-fetch'; const withNgrokUrl: ConfigPlugin = async (config) => { // 仅在开发环境执行 if (process.env.NODE_ENV !== 'development') { return config; } try { const res = await fetch('http://localhost:4040/api/tunnels'); const data = await res.json(); const publicUrl = data.tunnels.find(t => t.proto === 'https')?.public_url; if (publicUrl) { // 将地址注入到extra配置中 config.extra = { ...config.extra, apiUrl: publicUrl, }; } } catch (err) { console.error('插件中获取Ngrok URL失败:', err); } return config; }; export default withNgrokUrl;
- 在
app.config.ts中注册插件:
import { ExpoConfig, ConfigContext } from '@expo/config'; import withNgrokUrl from './plugins/withNgrokUrl'; export default ({ config }: ConfigContext): ExpoConfig => { return withNgrokUrl({ ...config, // 其他配置 }); };
方案3:运行时动态获取(适用于非配置项场景)
如果该地址不需要在app.config.ts中配置,仅在App运行时使用,可以在App初始化时异步获取并全局存储。
示例(使用React Context):
import { createContext, useContext, useEffect, useState } from 'react'; import { View, Text } from 'react-native'; const ApiUrlContext = createContext<string | null>(null); export const ApiUrlProvider = ({ children }: { children: React.ReactNode }) => { const [apiUrl, setApiUrl] = useState<string | null>(null); useEffect(() => { async function fetchUrl() { try { const res = await fetch('http://localhost:4040/api/tunnels'); const data = await res.json(); const publicUrl = data.tunnels.find(t => t.proto === 'https')?.public_url; setApiUrl(publicUrl || '默认地址'); } catch (err) { console.error('获取Ngrok URL失败:', err); setApiUrl('默认地址'); } } // 仅在开发环境执行 if (__DEV__) { fetchUrl(); } else { setApiUrl('生产环境地址'); } }, []); return ( <ApiUrlContext.Provider value={apiUrl}> {children} </ApiUrlContext.Provider> ); }; // 使用示例 export const useApiUrl = () => useContext(ApiUrlContext); // 在App入口包裹 export default function App() { return ( <ApiUrlProvider> <View> <Text>当前API地址: {useApiUrl()}</Text> </View> </ApiUrlProvider> ); }
内容的提问来源于stack exchange,提问作者Christian Merat
相关产品推荐
相关产品推荐

