react-native-router-dom
v0.0.5
Published
React Native 版 react-router-dom:基于 React Navigation 的配置式路由(Stack/Tabs/Outlet/Guard + Web linking)
Maintainers
Readme
react-native-router-dom
在 React Native 上提供一套尽量贴近 react-router-dom 的路由心智:用 URL(pathname/search/hash)描述当前页面,用配置式 routes 定义路由树,并在 RN 端映射到 React Navigation(Stack/Tabs),同时兼容 react-native-web 的地址栏直达、刷新、回退。
核心特性
- 配置式路由:用 RouteProps[] 描述路由树
- 统一跳转心智:navigate('/settings/detail') 可跨 Tabs/嵌套栈直达目标页面
- 嵌套路由: + index route
- Guard:全局 beforeEach + 路由级 guard
- Web linking:支持 react-native-web URL 同步(直达/刷新/回退)
安装
npm i react-native-router-dom依赖说明
本库已内置 React Navigation 核心依赖(v7),你无需单独安装。
但你需要手动安装以下原生依赖(React Navigation 运行必需):
npm i react-native-screens react-native-safe-area-context⚠️ 注意:安装完成后,iOS 项目请务必执行
cd ios && pod install。
快速开始
1) 定义路由(routes)
import type { RouteProps } from 'react-native-router-dom';
import { Login } from './Login';
import { Home } from './Home';
export const routes: RouteProps[] = [
{ path: '/login', element: Login, options: { headerShown: false } },
{ path: '/home', element: Home, options: { title: '首页' } },
];2) 渲染 RouterProvider
import React from 'react';
import { RouterProvider, createNativeRouter } from 'react-native-router-dom';
import { routes } from './routes';
export default function App() {
const router = React.useMemo(() => createNativeRouter(routes), []);
return <RouterProvider router={router} />;
}核心概念(强烈建议先看)
RouteProps(路由配置)
RouteProps 是你唯一需要维护的路由“真相来源”。它会被映射成 React Navigation 的一棵 Navigator 树。
类型:
type RouteType = 'stack' | 'tabs';
type RouteMeta = Record<string, unknown>;
type RouteElement = React.ComponentType<any> | React.ReactElement | null;
type RouteProps = {
path?: string;
index?: boolean;
element?: RouteElement;
children?: RouteProps[];
type?: RouteType;
navigatorOptions?: any;
options?: any;
meta?: RouteMeta;
guard?: RouteGuard;
};关键字段:
- path:
- 绝对 path:以 / 开头,例如 /settings/detail
- 相对 path:不以 / 开头,例如 detail,会拼接到父路由 path 后
- 通配:/docs/*(匹配任意后续片段)
- 动态参数:/user/:id
- index:对齐 react-router 的 index route(匹配父路由的 path;此时 path 可省略)
- element:页面组件(ComponentType / ReactElement / null)
- children:嵌套路由
- type:
- 'tabs':该节点的 children 会被渲染为 Tab.Navigator
- 'stack'(默认):该节点的 children 会被渲染为 Stack.Navigator
- meta:任意元信息(常用于 Guard)
- guard:路由级守卫(从父到子依次执行)
- navigatorOptions:React Navigation Navigator 级 props(原样透传到 Tab.Navigator / Stack.Navigator)
- options:React Navigation Screen options(原样透传到 Tab.Screen / Stack.Screen)
重要约束:
- leaf route(没有 children 的路由)必须有 element,否则会在启动时抛错
路由构建规则(理解这些能少踩坑)
- 每个非 index route 必须提供 path;否则会抛错:Route 缺少 path(或未标记 index: true)
- leaf route 必须有 element;否则会抛错:Route "/xxx" 缺少 element(leaf route 必须可渲染)
- index route 的“路由语义 path”会等于父路由的 path(用于匹配/生成 URL)
- Tabs 的初始页面选择规则:优先选择第一个非 index 子路由
- Stack 的初始页面选择规则:优先选择第一个 index 子路由
- 内部会为每个 route 生成全局唯一的 screenName;当配置导致 screenName 冲突时会抛错:重复的 screenName
Tabs/Stack 的 React Navigation 参数透传(与官方 API 对齐)
为了避免“改一个 TabBar 样式就要改源码发包”,本库将 React Navigation 的参数分成两层透传:
- Navigator 级:用
navigatorOptions,会原样透传到 Tab.Navigator / Stack.Navigator - Screen 级:用
options,会原样透传到 Tab.Screen / Stack.Screen
示例:调整 TabBar 图标/文字布局方向(官方 screenOptions)
import { Image } from 'react-native';
import type { RouteProps } from 'react-native-router-dom';
const Home = () => null;
export const routes: RouteProps[] = [
{
path: '/main',
type: 'tabs',
navigatorOptions: {
screenOptions: {
tabBarLabelPosition: 'beside-icon',
tabBarItemStyle: { flexDirection: 'row' },
},
},
children: [
{
path: '/home',
element: Home,
options: {
title: '首页',
tabBarLabel: '首页',
tabBarIcon: ({ focused, size }) => (
<Image
source={focused ? require('./home-active.png') : require('./home.png')}
style={{ width: size, height: size }}
resizeMode="contain"
/>
),
},
},
],
},
];location(当前路由状态)
useLocation() 会返回当前的 RouteLocation:
- pathname:/settings/detail
- search:?x=1
- hash:#top
- params:动态参数与通配参数(:id、*)
- matches:从父到子匹配到的 route 列表(可用于 Guard 判定)
跳转与导航
useNavigate(推荐)
import { useNavigate } from 'react-native-router-dom';
const navigate = useNavigate();
await navigate('/profile'); // 绝对跳转
await navigate('/user/42?x=1#top'); // search/hash 也会透传
await navigate(-1); // 返回动画与转场
本库直接复用 React Navigation 的动画能力。你不需要学习新的 API,只需要将 React Navigation 的配置写在 options 或 navigatorOptions 里即可。
1. 单个页面配置动画
在 RouteProps 中使用 options 字段,这相当于 <Stack.Screen options={...} />。
{
path: '/login',
element: Login,
options: {
// 设为模态窗效果(从下往上弹出)
presentation: 'modal',
// 简单的淡入淡出
animation: 'fade',
},
}2. 全局/Stack 级配置动画
在 RouteProps 中使用 navigatorOptions 字段,这相当于 <Stack.Navigator screenOptions={...} />。
{
path: '/', // 根路由通常是 stack
navigatorOptions: {
screenOptions: {
// 让该 stack 下所有页面默认都是 slide_from_right
animation: 'slide_from_right',
// 统一配置头部样式
headerStyle: { backgroundColor: 'white' },
},
},
children: [
// ... 子路由都会继承这个动画配置
]
}3. 常用动画预设(Native Stack)
React Navigation 提供了丰富的预设动画(animation 属性):
'default': 平台默认动画(iOS 右侧滑入,Android 底部浮现或淡入)'fade': 淡入淡出'slide_from_right': 从右侧滑入(类似 iOS 默认)'slide_from_bottom': 从底部滑入(类似 Android 默认)'simple_push': 标准 push 动画
更多高级配置(如
transitionSpec)请参考 React Navigation 文档。
相对跳转说明:
- navigate('detail') 会基于“当前 committed location.pathname”做相对解析
- committed location 由 在导航状态变更后维护
- 如果你在 RouterProvider ready 之前调用,可能会以 '/' 作为基准
useRouter(push/replace/back 的别名集合)
import { useRouter } from 'react-native-router-dom';
const router = useRouter();
await router.push('/home');
await router.replace('/login');
router.back();generatePath(动态参数生成)
import { generatePath } from 'react-native-router-dom';
generatePath('/user/:id', { id: '42' }); // /user/42
generatePath('/docs/*', { '*': 'a/b' }); // /docs/a/b组件
RouterProvider
RouterProvider 是整个路由系统的宿主,负责:
- 持有 router 实例并通过 Context 暴露给 Hooks/Link/Navigate
- 将 RouteProps[] 映射成 React Navigation 的 Navigator 树
- 处理 onStateChange,运行 Guard,并维护当前 committed location
- 在 Web 端连接 React Navigation linking 与浏览器地址栏
类型:
import type { RouterProviderProps } from 'react-native-router-dom';
type RouterProviderProps = {
/** 已创建好的 Router;与 routes 互斥,优先使用该值 */
router?: NativeRouter;
/** 路由配置;不传 router 时必传 */
routes?: RouteProps[];
/** 全局守卫(等价于 createNativeRouter(..., { beforeEach })) */
beforeEach?: RouteGuard;
/** 自定义 React Navigation linking;不传时会自动基于 routes 生成 */
linking?: any;
/** linking 前缀,仅在 linking 未传时生效,例如 ['https://example.com'] */
prefixes?: string[];
/**
* Web 路由模式(只在 linking 未传时生效)
* - 'history': HTML5 History(默认)
* - 'hash': Hash 模式(#/path)
*/
linkingType?: 'history' | 'hash';
};用法示例:直接传 routes(简单场景推荐):
import { RouterProvider } from 'react-native-router-dom';
import { routes } from './routes';
export default function App() {
return <RouterProvider routes={routes} />;
}用法示例:显式创建 router(方便做 SSR 或测试):
import React from 'react';
import { RouterProvider, createNativeRouter } from 'react-native-router-dom';
import { routes } from './routes';
export default function App() {
const router = React.useMemo(() => createNativeRouter(routes, { beforeEach }), []);
return <RouterProvider router={router} />;
}Outlet(嵌套路由)
- 父路由 element 中渲染 ,子路由页面会在 Outlet 位置渲染
- + useOutletContext() 传递布局上下文
类型:
type OutletProps = {
/** 传给子路由的上下文数据,useOutletContext<T>() 可获取 */
context?: unknown;
};用法示例:布局向子页面透传数据:
import { Outlet, useOutletContext } from 'react-native-router-dom';
function SettingsLayout() {
const user = { name: 'Alice' };
return <Outlet context={user} />;
}
function SettingsDetail() {
const user = useOutletContext<{ name: string }>();
return <Text>当前用户:{user.name}</Text>;
}Link / NavLink
import { Link, NavLink } from 'react-native-router-dom';
<Link to="/settings">去设置</Link>
<NavLink to="/home">
{({ isActive }) => (isActive ? '当前页' : '去首页')}
</NavLink>LinkProps:
import type { PressableProps } from 'react-native';
type LinkProps = Omit<PressableProps, 'children'> & {
/** 目标路径,支持绝对/相对、search、hash 等 */
to: string;
/** 是否使用 replace 语义(默认 push) */
replace?: boolean;
/** 额外透传给导航参数(会进入 React Navigation route.params) */
params?: Record<string, any>;
/** 渲染内容,通常是 Text 或 View */
children?: React.ReactNode;
};NavLinkProps:
type NavLinkProps = Omit<LinkProps, 'children'> & {
/** 是否使用“完全匹配”模式(类似 react-router 的 end) */
end?: boolean;
/** 支持函数 children,便于按 active 状态定制样式 */
children?: React.ReactNode | ((args: { isActive: boolean }) => React.ReactNode);
};NavLink 的 isActive 规则:
- end=true:仅当 current === target 时 active
- end=false(默认):current === target 或 current 以
${target}/开头时 active - target 为相对路径时,会基于当前 pathname 解析后再判断
Navigate(声明式跳转)
import { Navigate } from 'react-native-router-dom';
export function Jump() {
return <Navigate to="/home" replace />;
}类型:
type NavigateProps = {
/** 目标路径,支持绝对/相对、search、hash 等 */
to: string;
/** 是否 replace 当前历史记录(默认 false,即 push) */
replace?: boolean;
/** 透传给目标页面的 params(React Navigation route.params) */
params?: Record<string, any>;
};Navigate 本质上是一个副作用组件:
- 渲染时立即调用 useNavigate() 触发跳转,并返回 null
- 常用于 routes 配置里作为 element,或某些中转页面
Hooks(API 参考)
useNativeRouter
const router = useNativeRouter();- 只能在 内使用,否则会抛错
- 返回 NativeRouter 实例,包含:
- routes:当前 RouteProps[]
- navigationRef:React Navigation 的 NavigationContainerRef
- beforeEach(fn):注册全局守卫,返回取消函数
- navigate(to, options?) / push(to, params?) / replace(to, params?) / back()
- getCurrentLocation()
适合做一些底层集成场景,例如自定义返回行为等。
useRouter
const { push, replace, back, navigate } = useRouter();- 是对 useNativeRouter 的“瘦包装”,只暴露最常用的导航方法
- 适用于大多数业务组件
useNavigate
const navigate = useNavigate();
await navigate('/profile');
await navigate(-1); // 返回
await navigate('/user/42?x=1#top', { replace: true, params: { from: 'home' } });签名:
type NavigateFn = (to: string | number, options?: NavigateOptions) => Promise<boolean>;
type NavigateOptions = {
replace?: boolean;
params?: Record<string, any>;
};- to 为 number 且 < 0 时等价于 back()
- 返回 Promise,表示是否成功触发导航(未匹配到路由、NavigationContainer 未 ready 等情况会返回 false)
useLocation
const location = useLocation();
// location: RouteLocation | nullRouteLocation 结构:
type RouteLocation = {
pathname: string;
search: string;
hash: string;
params: Record<string, string>;
matches: RouteMatch[];
};RouteMatch:
type RouteMatch = {
route: RouteProps;
pathname: string;
params: Record<string, string>;
};useParams
const params = useParams(); // Record<string, string>
const id = params.id;
const rest = params['*']; // 对应通配符 /docs/*- 来源于当前匹配到的 leaf route 的动态参数与通配符参数
useSearchParams
const [searchParams, setSearchParams] = useSearchParams({ page: '1' });
const page = searchParams.get('page'); // string | null
setSearchParams({ page: '2' }); // ?page=2
setSearchParams('page=3&sort=desc', { replace: true });签名:
function useSearchParams(
defaultInit?: string | Record<string, string> | URLSearchParams,
): [
URLSearchParams,
(nextInit: string | Record<string, string> | URLSearchParams, options?: { replace?: boolean }) => void,
];- defaultInit 仅在当前 search 没有某个 key 时作为默认值写入
- setSearchParams 会基于当前 pathname/hash 生成新 URL,并通过 navigate 跳转
useMatch
const match = useMatch('/settings/*');
// match: { pathname, params, pattern } | null- pattern 支持动态参数与通配符,语义与 RouteProps.path 一致
- 典型用途:根据某个子路由是否匹配来高亮菜单等
useOutletContext
const ctx = useOutletContext<{ name: string }>();- 获取父级 Outlet 通过 context 传下来的数据
Guard(路由守卫)
Guard 支持两层:
- 全局:router.beforeEach(fn) 或 createNativeRouter(routes, { beforeEach: fn })
- 路由级:route.guard
执行顺序:
- 先执行所有 beforeEach(按注册顺序)
- 再从父到子依次执行 matches 中每个 route.guard
返回值语义:
- true / void:放行
- false:拦截(停留在原页面;Tab 点击会被阻止并回滚)
- string:重定向到该 path(默认 replace)
- redirect(to, { replace? }):更显式的重定向对象
类型:
type RouteGuardContext = {
to: RouteLocation;
from: RouteLocation | null;
};
type RouteGuardResult = boolean | Redirect | string | void;
type RouteGuard = (
ctx: RouteGuardContext,
) => RouteGuardResult | Promise<RouteGuardResult>;示例:
import { redirect } from 'react-native-router-dom';
router.beforeEach(({ to }) => {
const requiresAuth = to.matches.some(m => (m.route.meta as any)?.requiresAuth);
if (!requiresAuth) return true;
return redirect('/login');
});示例:注册/取消注册 beforeEach
const dispose = router.beforeEach(async ({ to }) => {
if (to.pathname === '/login') return true;
return true;
});
dispose();示例:路由级 guard(只对该分支生效)
import { redirect, type RouteProps } from 'react-native-router-dom';
export const routes: RouteProps[] = [
{
path: '/guarded',
element: GuardedPage,
guard: async ({ to }) => {
if (await isLoggedIn()) return true;
return redirect('/login', { replace: true });
},
},
];Web linking(react-native-web)
RouterProvider 在 web 上会通过 React Navigation linking 与地址栏同步:
- history 模式(默认):
- 支持地址栏直达:直接访问 /settings/detail
- 支持刷新:刷新后仍停留在对应页面
- 支持回退:浏览器 back/forward 与导航状态同步
- 要求 dev server 开启 history fallback(例如 webpack-dev-server 的 historyApiFallback: true)
- hash 模式:
- URL 形如 http://localhost:8080/#/settings/detail
- 不依赖 history fallback(因为路径在 hash 里)
- 通过 RouterProvider 内部同步地址栏,避免 React Navigation 在 web 下重复拼接 hash
要点:
- 如果你需要自定义 scheme/prefix,可以用 createLinking(routes, { prefixes }) 并传给 RouterProvider 的 linking
- 如果你需要 hash 模式:<RouterProvider linkingType="hash" ... />
Demo(建议作为“可运行文档”使用)
目录:demo/
运行:
cd demo
npm i
# iOS / Android
npm run ios
npm run android
# Web
npm run webdemo 覆盖的验证路由(建议在 web 下逐个用地址栏直达 + 刷新验证):
- /home:Link/NavLink、useRouter、generatePath、跨 Tab/嵌套跳转
- /settings:Outlet + index route、useMatch、useOutletContext、useSearchParams
- /settings/detail:相对跳转(..)、navigate(-1)、Guard(需要登录)
- /params:useLocation/useSearchParams 示例
- /user/:id:useParams 动态参数
- /docs/:useParams()[''] 通配
- /guarded:route.guard 示例
- /jump: 示例(带 query/hash)
导出 API(总览)
createNativeRouter
import { createNativeRouter } from 'react-native-router-dom';
const router = createNativeRouter(routes, {
beforeEach: async ({ to, from }) => {
// 可做登录校验、埋点等
return true;
},
});签名:
type CreateNativeRouterOptions = {
beforeEach?: RouteGuard;
};
function createNativeRouter(
routes: RouteProps[],
options?: CreateNativeRouterOptions,
): NativeRouter;NativeRouter 关键字段:
- routes: RouteProps[]
- navigationRef: NavigationContainerRefWithCurrent
- beforeEach(guard: RouteGuard): () => void
- navigate(to: string | number, options?: NavigateOptions): Promise
- push(to: string, params?: Record<string, any>): Promise
- replace(to: string, params?: Record<string, any>): Promise
- back(): void
- getCurrentLocation(): RouteLocation | null
一般业务只需要通过 RouterProvider + Hooks/Link 使用;直接操作 NativeRouter 适合做底层集成。
createLinking
import { createLinking } from 'react-native-router-dom';
const linking = createLinking(routes, {
prefixes: ['https://example.com', 'myapp://'],
});
<RouterProvider routes={routes} linking={linking} />;签名:
type LinkingOptions = {
prefixes?: string[];
};
function createLinking(routes: RouteProps[], options?: LinkingOptions): {
prefixes: string[];
config: { screens: Record<string, any> };
};- 默认 prefixes 为 [''],即可直接用 /path 形式的 URL
- config.screens 结构与 React Navigation linking config 一致
redirect / isRedirect
import { redirect, isRedirect } from 'react-native-router-dom';
router.beforeEach(({ to }) => {
if (to.pathname.startsWith('/admin') && !isAdmin()) {
return redirect('/login', { replace: true });
}
});签名:
type Redirect = {
type: 'redirect';
to: string;
replace?: boolean;
};
function redirect(to: string, options?: { replace?: boolean }): Redirect;
function isRedirect(value: unknown): value is Redirect;generatePath
import { generatePath } from 'react-native-router-dom';
const url = generatePath('/user/:id', { id: user.id });
// /user/123签名:
function generatePath(pattern: string, params?: Record<string, any>): string;- pattern 支持 :id、:slug 和 * 通配符
类型导出(TypeScript)
常用类型:
- RouteProps:路由配置
- RouteType:'stack' | 'tabs'
- RouteMeta:Record<string, unknown>
- RouteGuard / RouteGuardContext / RouteGuardResult
- Redirect
- RouteLocation / RouteMatch
所有类型均从包入口导出,可直接:
import type {
RouteProps,
RouteLocation,
RouteMatch,
RouteGuard,
RouteGuardContext,
} from 'react-native-router-dom';在业务项目中,推荐尽量使用这些类型而不是自己手写结构,以便在库升级时获得更好的类型兼容性。
