metro.config.js
编辑页面
Metro 中可用配置的参考。
For the complete documentation index, see llms.txt. Use this file to discover all available pages.
查看更多关于 metro.config.js 的信息,请参阅 自定义 Metro 指南。
环境变量
Expo CLI 可以从 .env 文件中加载环境变量。要了解更多关于如何在 Expo CLI 中使用环境变量的信息,请参阅 环境变量指南。
EAS CLI 使用一种不同的环境变量机制,除非它在编译和打包时调用 Expo CLI。要了解更多关于 EAS 中的环境变量 的信息。
如果你正在迁移一个较旧的项目,那么应通过将以下内容添加到你的 .gitignore 中来忽略本地 env 文件:
# 本地 env 文件 .env*.local
禁用 dotenv 文件
可以通过在调用任何 Expo CLI 命令之前启用 EXPO_NO_DOTENV 环境变量,来完全禁用 Expo CLI 对 dotenv 文件的加载。
# 所有用户都可以运行 cross-env,然后接着运行 Expo CLI 命令- npx cross-env EXPO_NO_DOTENV=1 expo start# 或者,macOS 和 Linux 用户可以先定义环境变量,然后运行 npx,再接着运行 Expo CLI 命令- EXPO_NO_DOTENV=1 npx expo start禁用以 EXPO_PUBLIC_ 为前缀的客户端环境变量
以 EXPO_PUBLIC_ 为前缀的环境变量会在构建时暴露给应用。例如,EXPO_PUBLIC_API_KEY 将可通过 process.env.EXPO_PUBLIC_API_KEY 访问。
可以通过环境变量 EXPO_NO_CLIENT_ENV_VARS=1 来禁用客户端环境变量内联,这必须在执行任何打包之前定义。
# 所有用户都可以运行 cross-env,然后接着运行 Expo CLI 命令- npx cross-env EXPO_NO_CLIENT_ENV_VARS=1 expo start# 或者,macOS 和 Linux 用户可以先定义环境变量,然后运行 npx,再接着运行 Expo CLI 命令- EXPO_NO_CLIENT_ENV_VARS=1 npx expo startCSS
CSS 支持正在开发中,目前仅适用于 web。
Expo 支持你项目中的 CSS。你可以从任何组件中导入 CSS 文件。也支持 CSS Modules。
CSS 支持默认启用。你可以通过在 Metro 配置中设置 isCSSEnabled 来禁用此功能。
/** @type {import('expo/metro-config').MetroConfig} */ const config = getDefaultConfig(__dirname, { // 禁用 CSS 支持。 isCSSEnabled: false, });
全局 CSS
全局样式仅适用于 web,使用它会导致你的应用在原生平台上的视觉效果发生偏差。
你可以从任何组件中导入一个 CSS 文件。该 CSS 将应用于整个页面。
这里,我们将为类名 .container 定义一个全局样式:
.container { background-color: red; }
然后我们可以通过导入样式表并使用 .container 在组件中使用这个类名:
import './styles.css'; import { View } from 'react-native'; export default function App() { return ( <> {/* 使用 `className` 将样式分配给 React DOM 组件。 */} <div className="container">Hello World</div> {/* 在 React Native for web 中,使用以下语法的 `style` 来追加类名。 */} <View style={{ $$css: true, _: 'container', }}> Hello World </View> </> ); }
你也可以像导入任何 node 模块一样,导入库中提供的样式表:
// 将样式应用到整个应用。 import 'emoji-mart/css/emoji-mart.css';
- 在原生平台上,所有全局样式表都会被自动忽略。
- 全局样式表支持热重载,只需保存文件,变更就会生效。
使用 Expo Router 时,始终要在根 _layout.tsx 中导入全局 CSS。Expo Router 会从根布局开始遍历依赖图。在嵌套布局中导入 CSS 会导致 node_modules 中的 CSS 先于你的自定义样式加载,这可能会破坏你期望的样式顺序。
CSS Modules
原生平台的 CSS Modules 正在开发中,目前仅适用于 web。
CSS Modules 是一种将 CSS 作用域限定到特定组件的方法。这对于避免命名冲突以及确保样式只应用于预期组件非常有用。
在 Expo 中,可以通过创建一个扩展名为 .module.css 的文件来定义 CSS Modules。该文件可以从任何组件中导入。导出的值是一个对象,其键为类名,值为仅 web 端可用的作用域名称。可以使用 unstable_styles 导入来访问对 react-native-web 安全的样式。
CSS Modules 支持平台扩展名,允许你为不同平台定义不同样式。例如,你可以定义 module.ios.css 和 module.android.css 文件,分别为 Android 和 iOS 定义样式。你需要不带扩展名进行导入,例如:
例如,反转扩展名,App.ios.module.css 将无法工作,并会导致生成一个名为 App.ios.module 的通用模块。
你不能向 React Native 或 React Native for web 组件的
className属性传递样式。相反,你必须使用style属性。
import styles, { unstable_styles } from './App.module.css'; export default function Page() { return ( <> <Text style={{ // 这是 react-native-web 应用类名的方式 $$css: true, _: styles.text, }}> Hello World </Text> <Text style={unstable_styles.text}>Hello World</Text> {/* 仅 web 端使用: */} <p className={styles.text}>Hello World</p> </> ); }
.text { color: red; }
- 在 web 上,所有 CSS 值都可用。CSS 不会像 React Native Web 的
StyleSheetAPI 那样被处理或自动添加前缀。你可以使用postcss.config.js为你的 CSS 自动添加前缀。 - CSS Modules 在底层使用 lightningcss,请查看 issues 了解不受支持的功能。
PostCSS
可以通过在项目根目录添加 postcss.config.json 文件来自定义 PostCSS。该文件应导出一个返回 PostCSS 配置对象的函数。例如:
{ "plugins": { "tailwindcss": {} } }
postcss.config.json 和 postcss.config.js 都受支持,但 postcss.config.json 能提供更好的缓存效果。
Expo CLI 使用内置的 browserslist 支持自动处理 CSS 厂商前缀。请避免添加 autoprefixer,因为这会重复该功能并减慢打包速度。
更新后重置缓存
更改 PostCSS 或 browserslist 配置后,你需要清除 Metro 缓存:
- npx expo start --clear- npx expo export --clearbrowserslist
Expo 通过基于 Rust 的 CSS 解析器提供自动 browserslist 支持。你可以通过在 package.json 文件中添加 browserslist 字段来自定义 CSS 厂商前缀和浏览器支持。例如:
{ "browserslist": [">0.2%", "not dead", "not op_mini all"] }
SASS
Expo Metro 对 SCSS/SASS 提供 部分 支持。
要进行设置,请在项目中安装 sass 包:
- yarn add -D sass然后,确保在 metro.config.js 文件中已完成 CSS 设置。
- 安装了
sass后,不带扩展名的模块将按以下顺序解析:scss、sass、css。 - 仅对
sass文件使用预期的语法。 - 目前不支持从 scss/sass 文件内部导入其他文件。
Tailwind
标准 Tailwind CSS 仅支持 web 平台。若要实现通用支持,请使用 NativeWind 或 Uniwind 等库,它们允许使用 Tailwind CSS 创建带样式的 React Native 组件。
了解如何在你的 Expo 项目中配置和使用 Tailwind CSS。
扩展 Babel 转换器
Expo 的 Metro 配置使用自定义的 transformer.babelTransformerPath 值,以确保始终使用 expo-babel-preset 并支持 web/Node.js 环境。
如果你想扩展 Babel 转换器,请从 @expo/metro-config/babel-transformer 导入上游转换器,而不是 metro-react-native-babel-transformer。例如:
const upstreamTransformer = require('@expo/metro-config/babel-transformer'); module.exports.transform = async ({ src, filename, options }) => { // 对 SVG 文件执行一些自定义操作... if (filename.endsWith('.svg')) { src = '...'; } // 将源码传递给上游的 Expo 转换器。 return upstreamTransformer.transform({ src, filename, options }); };
自定义解析
Expo CLI 扩展了默认的 Metro 解析器,增加了 Web、Server 和 tsconfig 别名支持等功能。你也可以通过链式调用 config.resolver.resolveRequest 函数,来自定义 Metro 的默认解析行为。
const { getDefaultConfig } = require('expo/metro-config'); /** @type {import('expo/metro-config').MetroConfig} */ const config = getDefaultConfig(__dirname); config.resolver.resolveRequest = (context, moduleName, platform) => { if (moduleName.startsWith('my-custom-resolver:')) { // 将模块名解析为文件路径的逻辑... // 注意:如果无法解析,请抛出错误。 return { filePath: 'path/to/file', type: 'sourceFile', }; } // 确保你调用默认解析器。 return context.resolveRequest(context, moduleName, platform); }; module.exports = config;
与传统打包器不同,Metro 在所有平台上共享同一个解析器函数。因此,你可以在每次请求时使用 context 对象动态修改解析设置。
模拟模块
如果你希望某个平台上的某个模块为空,可以从解析器返回一个 type: 'empty' 对象。下面的示例会让 lodash 在 web 上为空:
const { getDefaultConfig } = require('expo/metro-config'); /** @type {import('expo/metro-config').MetroConfig} */ const config = getDefaultConfig(__dirname); config.resolver.resolveRequest = (context, moduleName, platform) => { if (platform === 'web' && moduleName === 'lodash') { return { type: 'empty', }; } // 确保你调用默认解析器。 return context.resolveRequest(context, moduleName, platform); }; module.exports = config;
这种技术等同于在 Webpack 或 Vite 中使用空的 externals,但额外的好处是可以针对特定平台。
虚拟模块
Metro 目前不支持虚拟模块。你可以使用的一种类似方法是在 node_modules/.cache/... 目录中创建一个模块,并将解析重定向到该文件。
下面的示例会在 node_modules/.cache/virtual/virtual-module.js 中创建一个模块,并将 virtual:my-module 的解析重定向到该文件:
const path = require('path'); const fs = require('fs'); const { getDefaultConfig } = require('expo/metro-config'); /** @type {import('expo/metro-config').MetroConfig} */ const config = getDefaultConfig(__dirname); const virtualPath = path.resolve(__dirname, 'node_modules/.cache/virtual/virtual-module.js'); // 在生成目录中创建虚拟模块... fs.mkdirSync(path.dirname(virtualPath), { recursive: true }); fs.writeFileSync(virtualPath, 'export default "Hello World";'); config.resolver.resolveRequest = (context, moduleName, platform) => { if (moduleName === 'virtual:my-module') { return { filePath: virtualPath, type: 'sourceFile', }; } // 确保你调用默认解析器。 return context.resolveRequest(context, moduleName, platform); }; module.exports = config;
这可用于通过自定义导入来模拟 externals。例如,如果你想将 require('expo') 重定向到类似 SystemJS.require('expo') 的自定义内容,你可以创建一个导出 SystemJS.require('expo') 的虚拟模块,并将 expo 的解析重定向到该文件。
自定义转换
转换在 Metro 中会被大量缓存。如果你更新了某些内容,请使用
--clear标志查看更新。例如,npx expo start --clear。
Metro 没有一个非常灵活的文件转换插件系统,因此改为使用 babel.config.js 和 caller 对象来自定义转换。
module.exports = function (api) { // 获取 Expo CLI 正在为其转换的平台。 const platform = api.caller(caller => (caller ? caller.platform : 'ios')); // 检测打包操作是否使用 Hermes 引擎,例如 `'hermes'` | `undefined`。 const engine = api.caller(caller => (caller ? caller.engine : null)); // 是否正在为服务器环境打包,例如 API Routes。 const isServer = api.caller(caller => (caller ? caller.isServer : false)); // 是否正在为开发环境或生产环境打包。 const isDev = api.caller(caller => caller ? caller.isDev : process.env.BABEL_ENV === 'development' || process.env.NODE_ENV === 'development' ); // 确保配置不会被缓存,否则平台将不会更新。 api.cache(false); // 你也可以提供一种更健壮的 CONFIG 缓存失效方式: // api.cache.invalidate(() => platform); return { presets: ['babel-preset-expo'], plugins: [ // 基于平台添加一个插件... platform === 'web' && 'my-plugin', // 确保过滤掉假值。 ].filter(Boolean), }; };
如果 caller 没有 engine、platform、bundler 等信息,那么请确保你在转换器中使用的是 @expo/metro-config/babel-transformer。如果你使用的是自定义转换器,那么它可能需要继承 Expo 转换器。
尽可能始终在解析器中实现自定义逻辑,因为缓存会简单得多,也更容易推理。例如,如果你需要重映射一个 import,那么用解析器解析到一个静态文件会比解析所有可能的导入方式并用转换器重映射它们更简单、更快。
始终使用 babel-preset-expo 作为默认的 Babel 预设,这可确保转换始终与 Expo 运行时兼容。babel-preset-expo 会在内部使用所有 caller 输入,为给定的平台、引擎和环境进行优化。
按需文件系统
Expo 使用 metro-file-map 的一个分支(@expo/metro-file-map)来发现、监听并按需解析源文件。
Metro 在启动时会预先遍历 watchFolders 中列出的每个目录。在 monorepo 中,这通常意味着会遍历每个工作区。启用按需文件系统访问后,文件映射可以在解析器请求时,按需读取 watchFolders 之外的文件。这样你就可以安全地减少 watchFolders 条目的数量,在不破坏访问项目根目录之外导入的前提下,以减少文件监听覆盖范围来换取更快的启动时间。
按需遍历还允许将符号链接解析到 monorepo 根目录之外,这为 Bun 和 pnpm 等包管理器增加了对 Global Virtual Store 的支持。
此功能默认启用,你可以在 应用配置 中将 experiments.onDemandFilesystem 设置为 false 来禁用它:
{ "expo": { "experiments": { "onDemandFilesystem": false } } }
Node.js 内置模块
当为服务器环境打包时,Expo 的 Metro 配置会基于当前 Node.js 版本自动支持外部化 Node.js 内置模块(fs、path、node:crypto 等)。如果 CLI 正在为浏览器环境打包,那么内置模块会先检查该模块是否已在本地安装,然后回退到一个空的 shim。例如,如果你安装了 path 以便在浏览器中使用,那么它可以被使用;否则,该模块会自动被跳过。
环境设置
这些环境变量不会在测试环境中定义。
Expo 的 Metro 配置会注入构建设置,这些设置可以通过环境变量在客户端 bundle 中使用。所有变量都会被内联,因此不能动态使用。例如,process.env["EXPO_BASE_URL"] 将不起作用。
process.env.EXPO_BASE_URL暴露了在experiments.baseUrl中定义的基础 URL。这在 Expo Router 中用于在部署时遵循生产基础 URL。
Bundle 拆分
Expo CLI 会在生产环境中根据异步导入自动将 web bundle 拆分为多个 chunk。此功能要求安装并在入口 bundle 的某处导入 @expo/metro-runtime(在 Expo Router 中默认可用)。
异步 bundle 的共享依赖会合并为单个 chunk,以减少请求数量。例如,如果你有两个导入了 lodash 的异步 bundle,那么该库会被合并到一个初始 chunk 中。
chunk 拆分启发式规则无法自定义。例如:
math.jsindex.jsexport function add(a, b) { return a + b; }
import '@expo/metro-runtime'; // 这将被拆分到一个单独的 chunk 中。 import('./math').then(math => { console.log(math.add(1, 2)); });
当你运行 npx expo export -p web 时,bundle 会被拆分为多个文件,并且入口 bundle 会被添加到主 HTML 文件中。@expo/metro-runtime 会添加用于加载并执行异步 bundle 的运行时代码。
Source map Debug ID
如果 bundle 导出时带有外部 source map,那么文件末尾会添加一个 Debug ID 注释,同时在 source map 中添加匹配的 debugId,以便将这些文件对应起来。如果没有导出 source map,或者使用的是内联 source map,那么不会添加此注释。
// <所有源代码> //# debugId=<deterministic chunk hash>
相关联的 *.js.map 或 *.hbc.map source map 将是一个包含等效 debugId 属性的 JSON 文件。debugId 会在 Hermes 字节码生成之前注入,以确保在所有情况下都能匹配。
debugId 是 bundle 内容的确定性哈希,但不包含外部 bundle 拆分引用。这与用于创建 chunk 文件名的值相同,只是格式化为 UUID。例如,431b98e2-c997-4975-a3d9-2987710abd44。
@expo/metro-config 会在 npx expo export 和 npx expo export:embed 期间注入 debugId。npx expo export:embed 中任何额外的优化步骤,例如 Hermes 字节码生成,都需要手动注入 debugId。
Metro require 运行时
你可以通过环境变量 EXPO_USE_METRO_REQUIRE=1 选择性启用自定义的 Metro require 实现。此运行时具有以下特性:
- 使用人类可读的字符串模块 ID,从而更容易跟踪缺失模块错误。
- 确定性的 ID,在不同运行之间以及不同模块之间保持一致(开发环境中的 React Server Components 需要此特性)。
- 移除了对旧版 RAM bundle 的支持。
魔法导入注释
从 SDK 52 开始在所有平台可用。
像 Workers 和 Node.js 这样的服务器环境支持在运行时导入任意文件,因此你可能希望保留 import 语法,而不是使用 Metro 的 require 系统。你可以在 import() 语句中使用 /* @metro-ignore */ 注释来禁用动态导入。
// 手动确保 `./my-module.js` 被包含在相对于模块的正确位置。 const myModule = await import(/* @metro-ignore */ './my-module.js');
Expo CLI 会跳过 ./my-module.js 依赖,并假定开发者已手动将其添加到输出 bundle 中。在内部,这用于导出自定义服务器代码,该代码会根据请求在不同文件之间动态切换。请避免在原生 bundle 中使用这种语法,因为在启用 Hermes 的 React Native 中通常无法使用 import()。
许多 React 库沿用了 Webpack 的 /* webpackIgnore: true */ 注释来实现类似行为。为了打通差异,我们也添加了对 Webpack 该注释的支持,但建议你在应用中使用 Metro 对应的注释。
ES 模块解析
本节从 SDK 53 开始适用于所有平台。
Metro 对 ES Module import 和 CommonJS require 使用不同的解析策略。
在此之前,Metro 使用经典的 Node.js 模块解析策略(与 v12 之前的 Node.js 版本相匹配),并做了一些额外调整来支持 ES 模块。在这种解析策略中,Metro 会从 node_modules 和 JS 文件中解析模块,必要时会省略扩展名,例如 .js,并使用 main、module 和 react-native 等 package.json 字段。
现在,在现代 ES Modules 解析策略下,Metro 会先从 node_modules 解析模块,然后匹配不同的 package.json 字段,例如 exports、包公开的子路径嵌套映射 和 main。
根据包的导入方式,这两种解析策略中的一种会被使用。通常,从 Node 模块中通过 import(而不是 require)导入的文件会使用 ES Modules 解析策略,并回退到常规的经典 Node.js 解析。未通过 ES Modules 解析的文件,或者通过 CommonJS require 导入的文件,会使用经典解析策略。
package.json:exports
在执行 ES Modules 解析时,Metro 会查看 package.json:exports 的条件映射。这是一个将导入子路径和条件映射到 Node 模块包中文件的映射。
例如,一个始终公开 index.js 文件,并且与 Metro 的经典 CommonJS 模块解析相匹配的包,可以使用 default 条件指定一个映射。
{ "exports": { "default": "./index.js" } }
不过,提供 CommonJS 和 ES Modules 双入口的包,可以提供带有 import 和 require 条件的映射。
{ "exports": { "import": "./index.mjs", "require": "./index.cjs" } }
默认情况下,Metro 会根据平台以及解析是从 CommonJS require 调用开始,还是从 ES Modules import 语句开始,来匹配不同的条件,并相应地切换条件。
对于原生平台,会添加 react-native 条件;对于 web 导出,会添加 browser 条件;对于服务器导出(例如 API routes 或 React Server functions),会添加 node、react-server 和 workerd 条件。这些条件的匹配顺序不是按定义顺序,而是按 package.json:exports 映射中的属性顺序进行匹配。
TypeScript 会独立于 Metro 执行 ES Module 解析,并且在其 compilerOptions.moduleResolution 配置选项设置为 "bundler"(这与 Metro 的行为更接近)或 "node16" / "nodenext" 时,也会遵循 package.json:exports 映射。不过,TypeScript 还会匹配 types 条件。因此,当一个包没有把 types 条件放在 exports 映射的最前面时,类型可能无法正确解析。
由于 exports 映射可能包含子路径,包导入可能不再需要匹配包的 modules 文件夹中的某个文件,而可能是一个“重定向”导入。如果在 package.json:exports 中进行了指定,那么导入 'package/submodule' 可能会匹配到与 node_modules/package/submodule.js 不同的文件。
{ "exports": { ".": "./index.js", "./submodule": "./submodule/submodule.js" } }
如果你遇到与新的 ES Modules 解析策略不兼容或尚未准备好的包,你可以通过修补其 package.json 文件,并添加或修正其 package.json:exports 条件映射来解决问题。不过,也可以通过禁用 unstable_enablePackageExports 选项,阻止 Metro 在解析中使用 package.json:exports 映射。
const { getDefaultConfig } = require('expo/metro-config'); /** @type {import('expo/metro-config').MetroConfig} */ const config = getDefaultConfig(__dirname); config.resolver.unstable_enablePackageExports = false; module.exports = config;
资源导入
当导入资源时,会创建一个虚拟模块来表示导入该资源所需的数据。
在原生平台上,资源将是一个数字 ID:1、2、3 等,可以使用 require("@react-native/assets-registry/registry").getAssetByID(<NUMBER>) 来查找。在 web 和 server 平台上,资源会根据文件类型而变化。如果文件是图片,那么资源将是 { uri: string, width?: number, height?: number };否则资源将是一个 string,表示该资源的远程 URL。从 SDK 55 开始,你可以使用 String(asset) 获取 web 上任何资源的公开 URL,这不包括不能包含 toString 函数的 React Server Component 环境。
资源可以按如下方式使用:
import { Image } from 'react-native'; import asset from './img.png'; function Demo() { return <Image source={asset} />; }
在 API 路由中,你始终可以假设资源的类型不会是数字:
import asset from './img.png'; export async function GET(req: Request) { const ImageData = await fetch( new URL( // 访问资源 URI。 asset.uri, // 追加到当前请求 URL 的 origin。 req.url ) ).then(res => res.arrayBuffer()); return new Response(ImageData, { headers: { 'Content-Type': 'image/png', }, }); }
Web workers
重要 此功能处于 alpha 阶段,可能会有破坏性变更。
new Worker(new URL('./worker', window.location.href));
Expo Metro 提供了实验性的 web worker 支持。此功能目前仅支持 web,无法在原生平台上工作,在原生平台上使用会触发错误 "Property 'Worker' doesn't exist"。
Web worker 可用于将工作卸载到 web 上的独立线程,从而让主线程保持响应。这对于计算密集型任务很有用,例如图像处理、密码学或其他否则会阻塞主线程的任务。
Worker 可以使用 Blob 以内联方式生成,但有时你可能希望利用 TypeScript 等现代特性,或导入其他模块。
Web worker 依赖于 Expo 的 bundle 拆分支持,这意味着你需要使用 Expo Router,或者安装并导入 @expo/metro-runtime。你也不能在使用 web worker 时设置环境变量 EXPO_NO_METRO_LAZY=1。
请考虑以下一个将数字翻倍的 worker 示例:
self.onmessage = ({ data }) => { const result = data * 2; // 示例:将数字翻倍 self.postMessage(result); };
这个 worker 文件可以在主应用中作为 Worker 导入:
// worker 的类型是 `Worker` const worker = new Worker(new URL('./worker', window.location.href)); worker.onmessage = ({ data }) => { console.log(`Worker 响应:${data}`); }; worker.postMessage(5);
在幕后,Expo CLI 正在生成如下代码:
const worker = new Worker( new URL('/worker.bundle?platform=web&dev=true&etc', window.location.href) );
生成的 bundle URL 会根据开发/生产环境而变化,以确保 worker 被正确加载和打包。与传统的 bundle 拆分不同,worker 文件需要包含其自身所有模块的副本,并且不能依赖主 bundle 中的公共模块。
原生 API Worker 传统上在 React Native 中不可用,Expo SDK 也没有提供,因此即使这个打包特性在技术上适用于所有平台,它实际上只对 web 有用。如果你希望也支持原生平台,理论上可以编写一个原生 Expo 模块来为 Worker API 提供 polyfill。或者,你也可以在 React Native Reanimated 中使用 "worklet" API,将工作卸载到原生上的独立线程。
或者,你可以先把转换后的 JS 文件放入 public 目录,然后在 worker 导入中使用变量,通过公开路径导入 Workers:
// 将避免转换,并直接使用公开路径。 const worker = new Worker('/worker.js'); // 变量会破坏转换,导致使用字面量路径而不是转换后的路径。 const path = '/worker.js'; const anotherWorker = new Worker(new URL(path, window.location.href));
在 Worker 构造函数中使用变量不支持打包。若要检查内部 URL,你可以使用内部语法 require.unstable_resolveWorker('./path/to/worker.js') 来获取 URL 片段。
现有的 React Native 应用
本指南是有版本的,在升级/降级 Expo 时需要重新审视。或者,使用 Expo Prebuild 进行全自动设置。
不使用 Expo Prebuild 的项目必须配置原生文件,以确保始终使用 Expo Metro 配置来打包项目。
这些修改旨在分别将 npx react-native bundle 和 npx react-native start 替换为 npx expo export:embed 和 npx expo start。
metro.config.js
确保 metro.config.js 继承自 expo/metro-config:
const { getDefaultConfig } = require('expo/metro-config'); const config = getDefaultConfig(__dirname); module.exports = config;
android/app/build.gradle
Android 的 app/build.gradle 必须配置为在生产打包时使用 Expo CLI。修改 react 配置对象:
ios/<Project>.xcodeproj/project.pbxproj
在你的 ios/<Project>.xcodeproj/project.pbxproj 文件中,替换以下脚本:
"Start Packager" 脚本
移除 "Start Packager" 脚本。必须在运行应用之前或之后使用 npx expo 启动开发服务器。
"Bundle React Native code and images" 脚本
或者,在 Xcode 项目中,选择 "Bundle React Native code and images" 构建阶段,并添加以下修改:
你可以设置
CLI_PATH、BUNDLE_COMMAND和ENTRY_FILE环境变量来覆盖这些默认值。
自定义入口文件
默认情况下,React Native 仅支持使用根目录下的 index.js 文件作为入口文件(或平台特定变体,例如 index.ios.js)。Expo 项目允许使用任意入口文件,但这需要额外的裸工作流配置。
开发
可以通过使用 expo-dev-client 包来启用开发模式入口文件。或者你也可以添加以下配置:
生产
在你的 ios/<Project>.xcodeproj/project.pbxproj 文件中,替换 "Bundle React Native code and images" 脚本,以便根据 Metro 设置 $ENTRY_FILE:
Android 的 app/build.gradle 必须配置为使用 Metro 模块解析来查找根入口文件。修改 react 配置对象: