排查构建错误和崩溃
编辑页面
使用 EAS Build 时排查构建错误和崩溃的参考指南。
For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.
当出问题时,它很可能会以以下两种方式之一出错:
- 你的构建会失败。
- 构建会成功,但会遇到运行时错误,例如,在你运行它时崩溃或卡住。
关于缩小错误来源范围的所有标准建议在这里都适用;本文档提供了一些可能在你常规排查流程和技巧之外有用的信息。故障排查是一门艺术,你可能需要创造性地思考。
查找相关的错误日志
在继续之前,你需要确认自己已经找到了错误信息并且读过了它。具体怎么做会因你是在排查构建失败还是运行时错误而有所不同。
运行时错误
属于此类别的常见问题包括:“我的应用在本地运行良好,但一旦我运行构建就会立即崩溃”或“我的应用在 Expo Go 中可以正常工作,但在我的构建中会卡在启动画面”。当你的应用构建成功,但在运行时崩溃或卡住时,这被视为运行时错误。
请参阅调试指南中的“生产环境错误”部分,以了解当你的发布构建在运行时崩溃时如何查找日志。
如果通过这种方法仍然找不到任何有用的信息,请尝试逐步缩小崩溃来源。
构建错误
前往你的构建详情页面(如果你还没有打开它,可以在 构建仪表板 中找到它),然后点击任何失败的构建阶段,将其展开。通常,最早出现错误的阶段会包含最有用的信息,而后续失败的阶段往往是由第一个错误级联导致的。
无论处于哪个阶段,日志条目前缀带有 [stderr] 很常见,但请记住,这并不一定意味着这些日志指向错误;CLI 工具经常使用 stderr 来输出警告和其他诊断信息。
例如,在你的 Android 构建中,你可能会看到类似这样的内容:
[stderr] 注意:/build/workingdir/build/app/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageModule.java 使用或重写了已弃用的 API。[stderr] 注意:使用 -Xlint:deprecation 重新编译以获取详细信息。虽然你可能想进一步跟进这个警告,也可能不想,但它并不是导致构建失败的原因。那么,如何判断哪些日志才是真正的责任方呢?如果你在构建一个裸工作流项目,你应该已经很擅长识别这类问题了。如果你在构建一个 托管项目,这可能会比较棘手,因为你并不直接与原生代码交互,只编写 JavaScript。
一个好的方向是判断构建失败是由原生错误还是 JavaScript 错误导致的。当你的构建因 JavaScript 构建错误而失败时,通常会看到类似这样的信息:
❌ Metro 遇到一个错误:无法解析模块 ./src/Routes,来源于 /Users/expo/workingdir/build/App.js这个特定错误意味着应用正在导入 ./src/Routes,但它没有被找到。原因可能是 Git 中的文件名大小写与开发者文件系统中的不同(例如,Git 中是 routes.js,而不是 Routes.js),或者项目有一个构建步骤,但它没有配置为在 EAS Build 上运行。在这种情况下,事实证明 ./src/Routes 原本是要导入 ./src/Routes/index.js,但这个路径不小心被开发者的 .gitignore 排除了。
需要注意的是,对于 iOS 构建,构建详情页面只会显示一部分日志,因为 xcodebuild 的完整输出可能达到约 10MB。有时有必要打开完整的 Xcode 日志来查找你需要的信息;例如,当 JavaScript 构建失败但你在构建详情页面上看不到任何有用信息时。要打开完整的 Xcode 日志,请在构建完成后滚动到构建详情页面底部,然后点击查看或下载它们。
如果你正在开发一个托管应用,并且构建错误是原生错误而不是 JavaScript 错误,那么这很可能是由 config plugin 或项目中的某个依赖导致的。请留意日志中自上次成功构建以来你新增的任何包。运行 npx expo-doctor,以确定项目中的 Expo SDK 依赖版本与你的 Expo SDK 版本是否兼容。
有了错误日志,你通常可以开始修复构建问题,或者在 论坛 和 GitHub issues 中搜索相关包以深入排查。下面列出了一些常见问题来源。
你是否在使用 monorepo?
Monorepo 非常有用,但它们也会带来自己的一套问题。需要将整个 monorepo 上传到 EAS Build 构建器,完成设置,然后运行构建。
EAS Build 更像是典型的 CI 服务,因为我们需要的是源代码,而不是编译后的 JavaScript bundle 和清单。EAS Build 对 Yarn workspaces 提供一流支持,而 使用其他 monorepo 工具时,结果可能会有所不同。
有关更多信息,请参阅 使用 monorepo。
内存不足(OOM)错误
如果你的构建在 Gradle 日志中出现 "Gradle build daemon disappeared unexpectedly (it may have been killed or may have crashed)",这是因为负责为应用 JavaScript 打包的 Node 进程被终止了。
这通常表明你的应用 bundle 非常大,这会让整体应用二进制文件更大,并导致启动时间变慢,尤其是在低端 Android 设备上。有时,当较大的文本文件被当作源代码处理时也会出现这个错误,例如,如果你有一个包含 1MB+ HTML 字符串、用于加载到 webview 中的 JavaScript 文件,或者一个大小相近的 JSON 文件。
要确定 bundle 的大小以及查看大小来源的分布情况,请使用 Expo Atlas。
要提高 EAS Build 构建器的内存限制,请在 eas.json 中使用 large resource class。更多信息请参阅 Android-specific resource class 和 iOS-specific resource class。
不存在任何文件的错误
当你运行 eas build 时,项目文件会被上传到 Expo 的构建服务器。然而,.gitignore 中提到的任何文件或目录都不会被上传。这样做是有意为之,目的是防止 API 密钥等敏感信息暴露在应用代码中。
如果你的项目导入了 .gitignore 中列出的文件,构建将会失败,并出现 None of these files exist 错误。你可以通过以下几种方式解决此错误:
-
移除被忽略文件的导入语句并测试你的项目。如果项目按预期运行,那么该导入语句可能已经过时或未被使用。
-
将 Metro 无法解析的任何文件或目录从 .gitignore 中移除。不过,这会带来安全风险,因为这些文件中包含的任何敏感信息现在都会出现在项目的源代码和 Git 提交历史中。
-
使用
base64对文件进行编码,将该字符串保存为 secrets,并在 EAS Build hook 中创建该文件。更多信息请参阅 如果文件被 gitignore,如何将其上传到 EAS Build? -
重构源代码,避免在客户端导入敏感文件。如果某个文件是第三方提供商自动生成的代码,并且该提供商已经自动将文件列入你的 .gitignore,那么该文件很可能包含敏感信息。你不应在客户端包含它。在应用开发过程中,请确保遵循安全实践,例如使用环境变量或通过后端提供这些内容。更多信息请参阅 在环境变量中使用 secrets。
比较构建日志
当之前成功的 EAS Build 开始失败时,找出两次构建之间发生了哪些变化,有助于定位根本原因。EAS Build 详情页面上的 Compare 按钮可以帮助你并排比较两个构建,显示它们在构建日志和配置上的差异。
要比较两个构建:
- 在失败构建的 EAS dashboard 中打开构建详情页面
- 点击 Compare 按钮打开比较弹窗
- 输入你想进行比较的构建的 build ID 或完整 build URL。这将是之前成功的构建。
- 点击 Compare。
上面的截图展示了比较视图,它在顶部显示两个构建及其元数据。这包括状态、环境、Expo SDK 版本等。在元数据下方,并排的日志比较会按每个构建阶段组织输出。
在每个构建阶段中,会使用指示标记来显示两个构建之间发生了什么变化:
- Changed (X lines):该阶段在两个构建中都运行了,但输出不同。
- + Added:该阶段只存在于比较构建中(原始构建在到达此阶段之前就失败了)。
- - Removed:该阶段只存在于原始构建中。
在上面的示例中,将右侧失败的构建与左侧成功的构建进行比较,可以看出已安装包的差异,并突出显示了 lock 文件不匹配的问题。
在本地验证你的 JavaScript bundles
当构建因 Task :app:bundleReleaseJsAndAssets FAILED(Android)或 Metro encountered an error(iOS)而失败时,这意味着 Metro bundler 在尝试将应用的 JavaScript 代码嵌入到应用二进制文件中时,无法完成打包。该错误信息通常后面会跟随语法错误或其他有关打包失败原因的详细信息。不幸的是,标准 React Native 项目被配置为在 Gradle/Xcode 构建步骤的后期执行此步骤,这意味着你可能需要花一段时间才能看到这个错误。
你可以在本地运行 npx expo export 来构建生产 bundle,从而绕过其他所有构建步骤,以便更快地看到这个错误。重复运行该命令,解决发现的任何语法错误或其他问题,直到 bundle 成功构建。然后再次尝试你的 EAS Build。
在本地验证你的项目是否能构建并运行
如果日志不足以立即帮助你理解并修复根本原因,那么现在该尝试在本地复现问题了。如果你的项目能在本地以 release 模式构建并运行,那么在 EAS Build 上也同样可以构建,只要以下条件都满足:
- 相关的 Build tool versions(例如 Xcode、Node.js、npm、Yarn)在两个环境中相同。
- 相关的 environment variables 在两个环境中相同。
- 上传到 EAS Build 的 archive 包含相同的相关源文件。
你可以使用 npx expo run:android 和 npx expo run:ios 命令在本地验证项目是否能构建,并将 variant/configuration 标志设置为 release,以尽可能真实地复现 EAS Build 上的执行过程。更多信息请参阅 Android build process 和 iOS build process。
# 在本地以 release 模式编译并运行 Android 应用- npx expo run:android --variant release# 在本地以 release 模式编译并运行 iOS 应用- npx expo run:ios --configuration Release如果使用 CNG,这些命令将运行
npx expo prebuild来生成用于编译的原生项目。等你完成排查后,你很可能希望 清理这些更改,除非你想直接开始管理这些项目,而不是按需生成它们。
你也可以改为运行本地构建
eas build --local— 该命令将执行一系列步骤,尽可能接近托管 EAS Build 服务远程运行的流程。它会将你的项目复制到临时目录,并在那里进行任何必要的更改。 了解如何进行设置并将其用于 调试。
如果你的原生工具链安装正确,但仍无法在本地机器上以 release 模式构建并运行项目,那么它在 EAS Build 上也不会构建成功。先在本地修复问题,然后再尝试 EAS Build。本文档中的其他建议可能有助于你在本地解决问题,但这通常需要一些原生工具链方面的知识,或者恰当地借助 Google、Stack Overflow 和 GitHub Issues。
你的机器上没有配置 Xcode 和 Android Studio?
如果你本地没有安装原生工具链,例如你没有 Apple 电脑,因此无法在你的机器上构建 iOS 应用,那么排查构建错误会更棘手一些。在本地做小改动并查看 EAS Build 结果的反馈循环,要比在本地执行同样步骤更慢,因为 EAS Build 构建器必须先设置环境、下载你的项目并安装依赖,然后才能开始构建。
如果你愿意并且能够配置相应的原生工具,请参阅 React Native 环境设置指南。
我的应用在本地能构建,但在 EAS Build 上不能
默认情况下,EAS Build 会采用相对直接的流程来构建你的应用(Android 或 iOS)。如果 npx expo run:android --variant release 和 npx expo run:ios --configuration Release 在本地可用,但你的构建失败了,那么现在需要缩小范围,找出你的机器上有哪些配置还没有为 EAS Build 上的项目设置好。
- 在新目录中重新
git clone一份你的项目,并让它运行起来,最好是在另一台机器上。留意所需的每一个步骤,并确认它们也已经为 EAS Build 配置好了。 - 检查你的 environment variables 是否已正确配置。
- 验证 Node.js、npm、Yarn、Xcode、Java 和其他工具的版本在两个环境中相同。
- 确保你上传到 EAS Build 的 archive 包含相同的相关源文件。
为什么我的生产应用和开发应用不一致?
你可以通过使用 npx expo start --no-dev 启动应用,来测试应用中 JS 部分在生产环境中的运行方式。这会告诉 bundler 在提供服务之前先压缩 JavaScript,尤其会移除受 __DEV__ 布尔值保护的代码。这会移除大部分日志、HMR、Fast Refresh 功能,并让调试更困难一些,但这样你可以更快地迭代生产 bundle。
仍然有问题吗?
本指南远非全面,且根据你的经验水平,你可能仍然在努力让你的应用正常工作。
如果你已经遵循了这里的建议,那么你现在已经处于一个很好的位置,可以向其他开发者描述你的问题并获得一些帮助。
如何提出一个好问题
加入我们的 Discord 和论坛,向社区和 Expo 团队寻求帮助。Expo 团队会尽最大努力回应高质量且表述清晰的问题和议题,但除非你已订阅 支持计划,否则不保证会有回复。为了确保 Expo 团队成员看到你的问题,你可以在 expo.dev/contact 提交工单。
当你寻求排查帮助时,请务必提供以下信息:
- 构建页面的链接。只有你的团队或 Expo 员工才能访问它。如果你想更公开地分享,可以截屏。如果你想更私密地分享,可以发送电子邮件至 secure@expo.dev,并在你于聊天或论坛中的求助请求里提到这一点。如果你是使用
eas build --local在本地执行此构建,则可以省略这一项,但请说明这一事实。 - 错误日志。任何你怀疑可能与构建或运行时错误相关的内容。如果你无法提供这些内容,请解释原因。
- 最小可复现示例或你的仓库链接。获得问题解决方案最快的方法是确保其他开发者能够复现它。如果你曾在团队中工作过,你就会从经验中知道这一点。在许多情况下,如果你无法提供可复现的示例,那么可能无法帮助你;即使能帮到,来回提问和回答的过程也会是对时间的低效利用。要了解如何创建可复现示例,请参阅 手动调试指南 和 Stack Overflow 的 最小可行可复现示例 指南。
尽量做到清晰、准确且有帮助。Stack Overflow 的 如何提出一个好问题 指南中提供的一般建议同样适用。