This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
自定义导航器
学习如何在 Expo Router 中构建自己的导航器,以及库作者如何将现有导航器与路由集成。
Expo Router 配备了用于最常见模式的导航器 — 堆栈、标签页、原生标签页 和 抽屉。当这些都不适用时,你可以自己构建导航器并将其作为布局使用,基于文件的路由、深度链接和类型化路由的功能与内置导航器完全相同。
🌐 Expo Router ships with navigators for the most common patterns — Stack, Tabs, Native tabs, and Drawer. When none of them fit, you can build your own navigator and use it as a layout, with file-based routing, deep linking, and typed routes working exactly as they do for the built-in navigators.
选择与你的目标相符的入口点:
🌐 Choose the entry point that matches your goal:
- 应用开发者 为一个应用构建导航器时使用
createStandardRouterNavigator。 - 库作者 为 Expo Router 和 React Navigation 提供可重用导航器时使用
integrateWithRouter。
信息 稳定的
createStandardRouterNavigator和integrateWithRouterAPI 可在 SDK 58 及更高版本 使用。在 SDK 56 和 SDK 57 中,请改用unstable_createStandardRouterNavigator和unstable_integrateWithRouter。
对于将路由呈现为网页模态叠加的堆栈导航器,请参见 构建自定义网页模态。
🌐 For a stack navigator that renders routes as web modal overlays, see Build custom web modals.
在你的应用中创建一个导航器
🌐 Create a navigator in your app
使用 createStandardRouterNavigator 将内容组件转换为可作为布局渲染的导航器。它需要两个必填参数:
🌐 Use createStandardRouterNavigator to turn a content component into a navigator you can render as a layout. It takes two required arguments:
NavigatorContent:一个渲染你的导航器 UI 的组件。它接收当前的导航state、每个屏幕的descriptors、用于导航的actions,以及用于发送事件的emitter。router:要使用的路由行为。从expo-router导入StackRouter用于堆栈式导航,或导入TabRouter用于标签式导航。
以下示例构建了一个最小的标签导航器:
🌐 The following example builds a minimal tab navigator:
返回的导航器有一个 .Screen 子项用于声明屏幕,所以你可以像使用其他布局一样在 _layout 文件中使用它:
🌐 The returned navigator has a .Screen child for declaring screens, so you can use it in a _layout file like any other layout:
NavigatorContent 接收的内容
🌐 What NavigatorContent receives
键入事件
🌐 Typed events
如果你的导航器会触发事件,请在 NavigatorContentProps 的第二个类型参数中声明它们。每个键是事件名称,其值描述事件的 data 以及它是否 canPreventDefault。然后 emitter.emit 会按照该映射进行类型检查——未知事件名称和不匹配的负载会被拒绝:
🌐 If your navigator emits events, declare them in the second type argument to NavigatorContentProps. Each key is an event name, and its value describes the event's data and whether it canPreventDefault. emitter.emit is then typed against that map — unknown event names and mismatched payloads are rejected:
type TabsContentProps = NavigatorContentProps< { title?: string }, { tabPress: { data: undefined; canPreventDefault: true } } >; function TabsContent({ emitter }: TabsContentProps) { emitter.emit({ type: 'tabPress', canPreventDefault: true }); // ... }
createStandardRouterNavigator 会根据组件推断事件映射,所以你在调用的时候不用再传一遍。对于不触发事件的导航器,可以省略第二个类型参数。
选项
🌐 Options
createStandardRouterNavigator 和 integrateWithRouter 都可以接受一个可选的 options 对象作为它们的第三个参数。使用 createProps 来派生导航器特定的属性,这些属性不属于标准的 state 和 actions:
🌐 Both createStandardRouterNavigator and integrateWithRouter accept an optional options object as their third argument. Use createProps to derive navigator-specific props that are not part of the standard state and actions:
export const Tabs = createStandardRouterNavigator(TabsContent, TabRouter, { createProps: ({ state, dispatch }) => ({ activeRouteKey: state.routes[state.index].key, preload: (name: string) => dispatch({ type: 'PRELOAD', payload: { name } }), }), });
在 NavigatorContentProps 的第四个类型参数中声明 createProps 返回的 props,这样 NavigatorContent 就能以类型化的方式接收它们:
🌐 Declare the props returned by createProps in the fourth type argument to NavigatorContentProps so NavigatorContent receives them in a typed way:
type TabsContentProps = NavigatorContentProps< { title?: string }, // No custom events in this example. Record<string, never>, // No custom navigator props in this example. object, // Props injected by `createProps`. { activeRouteKey: string; preload: (name: string) => void } >; function TabsContent({ activeRouteKey, preload }: TabsContentProps) { // ... }
信息
createProps接收处理后的 Expo Routerstate和原始dispatch。这些是内部使用的,版本之间可能有小的破坏性改动,所以在足够使用的情况下,优先使用传给NavigatorContent的state和actions。如果标准的state、actions或emitter缺少你需要的内容,在 GitHub 上提交一个 issue 。
集成现有导航器(库作者)
🌐 Integrate an existing navigator (library authors)
标准导航器 API
🌐 The standard navigator API
上面显示的 NavigatorContent 组件是一个 标准导航器。它实现了由 standard-navigation 包定义的最小、与框架无关的契约。你的内容所接收的 state、descriptors、actions 和 emitter 与上面的应用内导航器具有完全相同的 API。唯一的区别是是谁创建了导航器。
🌐 The NavigatorContent component shown above is a standard navigator. It implements a minimal, framework-agnostic contract defined by the standard-navigation package. The state, descriptors, actions, and emitter your content receives are exactly the same API as the in-app navigator above. The only difference is who creates the navigator.
createStandardRouterNavigator 是一个快捷方式,它会为你调用 createStandardNavigator(来自 standard-navigation)并在一步中将结果与 Expo Router 集成。作为库的作者,你可以自己调用 createStandardNavigator 并保持对导航器的引用:
因为 TabsContent 和 navigator 仅依赖于标准合同,相同的代码可以在 Expo Router、React Navigation 或任何实现它的其他宿主上运行。你只需编写一次导航器,并为每个框架提供一个轻量级的集成入口点。
🌐 Because TabsContent and navigator depend only on the standard contract, the same code runs on Expo Router, React Navigation, or any other host that implements it. You write the navigator once and ship a thin integration entry point per framework.
与 Expo 路由集成
🌐 Integrate with Expo Router
用 integrateWithRouter 把你的导航器接入 Expo Router:
🌐 Wire your navigator into Expo Router with integrateWithRouter:
返回的组件的工作方式和 createStandardRouterNavigator 的组件完全一样,包括 .Screen 子组件和相同的 选项。
🌐 The returned component works exactly like the one from createStandardRouterNavigator, including the .Screen child and the same options.
为内置导航器添加属性
🌐 Add props for a built-in navigator
信息 本节中的辅助工具在 SDK 58 及更高版本 可用。
当你的库封装了一个 Expo Router 导航器时,把它的 createProps 辅助工具传给 integrateWithRouter。这个辅助工具会添加 Expo Router 所期望的导航器特定行为。
🌐 When your library wraps an Expo Router navigator, pass its createProps helper to integrateWithRouter. The helper adds the navigator-specific behavior that Expo Router expects.
例如,整合一个像这样的 JavaScript 技术栈:
🌐 For example, integrate a JavaScript stack like this:
对于低层实现,可以使用 expo-router 中的 createBaseStackProps 或 createBaseTabProps,然后添加你的导航器需要的行为。
🌐 For a lower-level implementation, use createBaseStackProps or createBaseTabProps from expo-router and add the behavior your navigator needs.
库入口点
🌐 Library entry points
保持导航器内容和标准导航器与框架无关,然后为每个框架提供一个入口点,以便使用者导入与其应用匹配的集成:
🌐 Keep the navigator content and the standard navigator framework-agnostic, then expose one entry point per framework so consumers import the integration that matches their app:
.srcTabsContent.tsxNavigator UI implementing the standard navigator APIindex.tsRoot entry — exports the framework-agnostic navigatorreact-navigation.tsReact Navigation entry — integrates the same navigatorexpo-router.tsExpo Router entry — integrateWithRouter(navigator, ...)package.jsonMaps subpath exports to each framework entry将每个入口点映射到库的 package.json 中的 子路径导出,指向你的构建输出:
🌐 Map each entry point to a subpath export in your library's package.json, pointing at your build output:
然后,消费者为他们的框架导入该集成(例如,import { Tabs } from 'my-tabs/expo-router'),同时你在一个地方维护导航器逻辑。
🌐 Consumers then import the integration for their framework (for example, import { Tabs } from 'my-tabs/expo-router') while you maintain the navigator logic in one place.
学习如何将同一个标准导航器与 React Navigation 集成。
自定义路由行为
🌐 Customize router behavior
信息
extendRouter和extendRouterActions在 SDK 58 及更高版本 可用。
当你只需要处理或拒绝导航操作时,使用 extendRouterActions。返回一个结果来处理操作,使用 null 拒绝操作,或者用 undefined 让基础路由来处理它。
🌐 Use extendRouterActions when you only need to handle or reject navigation actions. Return a result to handle the action, null to reject it, or undefined to let the base router handle it.
当你需要自定义其他路由成员时,比如 actionCreators、getStateForRouteFocus 或 normalizeState,可以使用 extendRouter。你没有返回的成员会从基础路由继承。
🌐 Use extendRouter when you need to customize other router members, such as actionCreators, getStateForRouteFocus, or normalizeState. Members you do not return are inherited from the base router.
下面的例子添加了一个 CLEAR 动作和一个为它创建动作的函数:
🌐 The following example adds a CLEAR action and an action creator for it:
两个辅助工具都提供 baseRouter、options 和 nextKey。用 baseRouter 来委托现有的行为,options 用于读取传递给路由工厂的值,nextKey 在向状态添加路由时使用。
🌐 Both helpers provide baseRouter, options, and nextKey. Use baseRouter to delegate existing behavior, options to read values passed to the router factory, and nextKey when adding a route to the state.