错误处理和加载状态

学习在使用 Expo Router 时如何处理应用中的未匹配路由、错误和加载状态。


For the complete documentation index, see llms.txt. Use this file to discover all available pages.

本指南说明了在使用 Expo Router 时如何处理应用中的未匹配路由、错误和加载状态。

🌐 This guide specifies how to handle unmatched routes, errors, and loading states in your app when using Expo Router.

未匹配的路由

🌐 Unmatched routes

原生应用没有服务器,所以从技术上讲不存在 404 错误。然而,如果你在全局实现路由,那么处理缺失的路由是有意义的。这对于每个应用都是自动补齐的,但你也可以进行自定义。

🌐 Native apps don't have a server so there are technically no 404s. However, if you're implementing a router universally, then it makes sense to handle missing routes. This is done automatically for each app, but you can also customize it.

src/app/+not-found.tsx
import { Unmatched } from 'expo-router'; export default Unmatched;

这将渲染默认的 Unmatched。你可以导出任何你想渲染的组件。我们建议提供一个指向 / 的链接,这样用户可以返回主屏幕。

🌐 This will render the default Unmatched. You can export any component you want to render instead. We recommend having a link to / so users can navigate back to the home screen.

路由优先级

🌐 Route priority

在网页端,文件按以下顺序提供:

🌐 On web, files are served in the following order:

  1. public 目录中的静态文件。
  2. 应用目录中的标准和动态路由。
  3. 应用目录中的 API 路由
  4. 未找到的路由将最后返回 404 状态码。

错误处理

🌐 Error handling

Expo Router 支持微调错误处理,以便在未来实现更加有态度的数据加载策略。

🌐 Expo Router enables fine-tuned error handling to enable a more opinionated data-loading strategy in the future.

你可以从任何路由导出嵌套的 ErrorBoundary 组件,以使用 React 错误边界 拦截并格式化组件级错误:

🌐 You can export a nested ErrorBoundary component from any route to intercept and format component-level errors using React Error Boundaries:

src/app/home.tsx
import { View, Text } from 'react-native'; import { type ErrorBoundaryProps } from 'expo-router'; export function ErrorBoundary({ error, retry }: ErrorBoundaryProps) { return ( <View style={{ flex: 1, backgroundColor: "red" }}> <Text>{error.message}</Text> <Text onPress={retry}>Try Again?</Text> </View> ); } export default function Page() { ... }

当你导出一个 ErrorBoundary 时,该路由将被一个 React 错误边界有效地封装:

🌐 When you export an ErrorBoundary the route will be wrapped with a React Error Boundary effectively:

Virtual
function Route({ ErrorBoundary, Component }) { return ( <Try catch={ErrorBoundary}> <Component /> </Try> ); }

ErrorBoundary 不存在时,错误将被抛到最近的父级 ErrorBoundary,并接受 errorretry 属性。

🌐 When ErrorBoundary is not present, the error will be thrown to the nearest parent's ErrorBoundary and accepts error and retry props.

尚未完成的

🌐 Work in progress

React Native LogBox 需要以不那么激进的方式渲染,以便在有错误时继续开发。目前,它会在 console.errorconsole.warn 上显示。然而,理想情况下,它应该只在未捕获的错误上显示。

🌐 React Native LogBox needs to be presented less aggressively to develop with errors. Currently, it shows for console.error and console.warn. However, it should ideally only show for uncaught errors.

使用 Suspense 回退的加载状态

🌐 Loading states with Suspense fallback

Expo Router 会将每条路由封装在一个 React Suspense 边界中。你可以从布局文件中导出一个 SuspenseFallback 组件,以自定义在任何子路由被挂起时显示的加载界面:

🌐 Expo Router wraps each route in a React Suspense boundary. You can export a SuspenseFallback component from a layout file to customize the loading UI shown while any child route is suspended:

src/app/_layout.tsx
import { ActivityIndicator, View } from 'react-native'; import { Stack } from 'expo-router'; export function SuspenseFallback() { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <ActivityIndicator size="large" /> </View> ); } export default function RootLayout() { return <Stack />; }

当多个父布局定义了回退时,最近的父布局优先。

访问路由参数

🌐 Accessing route parameters

[SuspenseFallback](/versions/unversioned/sdk/router/#suspensefallback) 组件接收 routeparams 属性。你可以使用 params 来显示动态路由的特定上下文加载状态,例如:

🌐 The SuspenseFallback component receives route and params props. You can use params to display context-specific loading states for dynamic routes, for example:

src/app/(app)/_layout.tsx
import { ActivityIndicator, Text, View } from 'react-native'; import { Stack, type SuspenseFallbackProps } from 'expo-router'; export function SuspenseFallback({ params }: SuspenseFallbackProps) { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Text>Loading profile {params.id}...</Text> <ActivityIndicator size="large" /> </View> ); } export default function AppLayout() { return <Stack />; }

局限性

🌐 Limitations