React Router Declarative Mode 完整指南:用 `<BrowserRouter>` 与 `<Routes>` 实现最简声明式路由
React Router Declarative Mode 完整指南用BrowserRouter与Routes实现最简声明式路由【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router本指南聚焦 React Router 三种使用模式中最简单的Declarative Mode声明式模式它依托BrowserRouter等顶层 Router 组件与 JSX 形态的Routes/Route配置把 URL 匹配到 UI 组件并提供应用内导航与 URL 值读取能力。读完本文将掌握声明式应用的安装与骨架搭建、嵌套/布局/索引路由的编排、Link/NavLink/useNavigate的导航规范、useParams/useSearchParams/useLocation的 URL 值使用边界以及它与 Data Mode、Framework Mode 的分界线与升级路径。文中全部细节均来自当前仓库的官方文档docs/start/modes.md、docs/start/declarative 目录与 packages/react-router 源码。什么是 Declarative ModeReact Router 是一个多策略路由器同一套 API 之下有Declarative声明式、Data数据、Framework框架三种主要使用方式。正如 docs/start/modes.md 所描述的三种模式的 API 特性是additive叠加式的——从 Declarative 升级到 Data 再到 Framework本质是以放弃部分架构控制权为代价换取更多开箱即用的能力。因此选哪种模式取决于你希望 React Router 帮你做多少事而不是你想要怎样的部署形态。Declarative Mode 是其中最简单的一种使用BrowserRouter、HashRouter、MemoryRouter这类顶层路由组件包裹应用使用 JSX 形式的路由配置RoutesRoute提供 URL 匹配组件、应用内导航和 active 状态等基础能力Link、useNavigate、useLocation等不提供loader、action、fetcher 以及>import { createBrowserRouter, RouterProvider } from react-router; let router createBrowserRouter([ { path: /, Component: Root, loader: loadRootData, }, ]); ReactDOM.createRoot(root).render(RouterProvider router{router} /);而 Framework Mode 则在 Vite 配置中以routes.ts声明路由文件并配合types自动生成类型。声明式模式下这些都用不到——这正是它的价值最小依赖、最少概念。什么场景适合选 Declarative Modedocs/start/modes.md 给出的决策建议是出现以下情况优先考虑 Declarative Mode想把 React Router 用得尽可能简单从早期 React Router 版本迁移而来已经习惯BrowserRouter的写法数据层要么天然跳过 pending 状态如 local-first、后台数据同步/复制型架构要么已经拥有自己的一整套 pending/loading 抽象不需要路由层再来一套从 Create React App 迁移而来不过这种场景官方也提示可能更应该考虑 Framework Mode。快速开始安装与最小声明式应用文档 docs/start/declarative/installation.md 给出了一条最直接的起步路径。先用 Vite 官方模板创建一个 React 应用也可用你喜欢的任何方式初始化 React 工程npx create-vitelatest随后安装 React Routernpm i react-router最后在应用根部渲染一个BrowserRouter包裹整个应用import React from react; import ReactDOM from react-dom/client; import { BrowserRouter } from react-router; import App from ./app; const root document.getElementById(root); ReactDOM.createRoot(root).render( BrowserRouter App / /BrowserRouter, );一个声明式应用通常拥有两层结构顶层 Router 组件负责订阅浏览器历史、提供 Router ContextRoutes路由表负责把当前 URL 匹配到具体组件。作为对照lib/dom/lib.tsx 中BrowserRouter、HashRouter都实现在 DOM 层lib.tsx#L826、lib.tsx#L917分别面向historyAPI 与 URL hash 两种历史存储而MemoryRoutercomponents.tsx#L799在内存中维护历史栈适合测试与无浏览器环境。三者都渲染同样的 Router Context因此App内部的路由代码可以无缝切换。声明式路由的标准骨架当一个应用被判定为 Declarative Mode 后最常见的骨架如下摘自 .agents/skills/react-router/references/declarative-mode.md也是官方示例react-router/docs/start/declarative/routing.md的推荐写法import { BrowserRouter, Routes, Route } from react-router; function App() { return ( BrowserRouter Routes Route path/ element{Home /} / Route pathabout element{About /} / Route pathdashboard element{DashboardLayout /} Route index element{DashboardHome /} / Route pathsettings element{Settings /} / /Route /Routes /BrowserRouter ); }在代码识别层面声明式应用通常围绕以下 API 展开它们也正是该模式文档中明确列出的识别清单BrowserRouter/HashRouter/MemoryRouterRoutes/Routeelement{Component /}useRoutes注意Route组件本身也支持loader、action、lazy、Component等属性详见 docs/api/components/Route.md但在声明式路由器中不要给路由对象添加 loader/action——那是 Data/Framework Mode 的领域。路由配置从嵌套到 Splat在动手编辑路由前文档要求先通读 docs/start/declarative/routing.md并遵守以下规则使用Routes与Route完成路由配置用嵌套路由 Outlet实现共享布局用 index 路由充当父路由的默认子 UI按声明式路由文档使用路由参数params与 splat不要往声明式路由器里塞路由对象级 loader/action。嵌套路由与 Outlet父路由的 path 会自动拼接进子路由因此下面的配置同时产生/dashboard与/dashboard/settings两个 URLRoutes Route pathdashboard element{Dashboard /} Route index element{Home /} / Route pathsettings element{Settings /} / /Route /Routes子路由通过父组件中的Outlet/渲染import { Outlet } from react-router; export default function Dashboard() { return ( div h1Dashboard/h1 {/* 这里要么是 Home/ 要么是 Settings/ */} Outlet / /div ); }Layout Routes无 path 的嵌套Route不写path时就成为 layout route只为子路由建立新的嵌套层级但不向 URL 增加任何段。下例用MarketingLayout和ProjectsLayout共享外壳、却不改变 URL 语义Routes Route element{MarketingLayout /} Route index element{MarketingHome /} / Route pathcontact element{Contact /} / /Route Route pathprojects Route index element{ProjectsHome /} / Route element{ProjectsLayout /} Route path:pid element{Project /} / Route path:pid/edit element{EditProject /} / /Route /Route /RoutesIndex RoutesIndex 路由用index属性声明渲染进父路由的Outlet/扮演父 URL 上的默认子页面Routes Route path/ element{Root /} {/* 在 / 处渲染进 Root 的 outlet */} Route index element{Home /} / Route pathdashboard element{Dashboard /} {/* 在 /dashboard 处渲染进 Dashboard 的 outlet */} Route index element{DashboardHome /} / Route pathsettings element{Settings /} / /Route /Route /Routesindex 路由不能有子路由如果你确实需要既有默认 UI 又有子路由那应该改用 layout route。Route Prefixes一个带path但没有element的Route不会引入父级布局只是给子路由统一加一个路径前缀Route pathprojects Route index element{ProjectsHome /} / Route element{ProjectsLayout /} Route path:pid element{Project /} / Route path:pid/edit element{EditProject /} / /Route /RouteDynamic Segments动态段路径段以:开头即为动态段。匹配成功后解析出的值会以params形式提供给useParams等 APIRoute pathteams/:teamId element{Team /} /import { useParams } from react-router; export default function Team() { let params useParams(); // params.teamId }一个 path 可以同时有多个动态段Route path/c/:categoryId/p/:productId element{Product /} /import { useParams } from react-router; export default function CategoryProduct() { let { categoryId, productId } useParams(); // ... }一个实用提醒同一 path 中所有动态段名必须唯一。因为params对象是逐步填充的后出现的同名动态段会覆盖先前的值。Optional Segments可选段段尾加?可使该段可选动态段与静态段都支持Route path:lang?/categories element{Categories /} /Route pathusers/:userId/edit? element{User /} /Splats通配段以/*结尾的 path 会匹配/之后的一切字符包括额外斜杠俗称 catchall / star 段Route pathfiles/* element{File /} /let params useParams(); // params[*] 包含 files/ 之后的剩余 URL let filePath params[*];解构通配键时必须重命名常见做法是命名为splatlet { *: splat } useParams();导航Link、NavLink 与 useNavigate按 docs/start/declarative/navigating.md用户导航应用的三大入口是Link、NavLink与useNavigate。规则如下用户主动触发的应用内导航用Link或NavLink需要 active 样式时用NavLink事件回调或 effect 中的命令式跳转用useNavigate除非故意触发整页刷新否则内部导航不要使用裸a href。NavLink内置 active 状态NavLink在激活时会自动挂上.active类名配合 CSS 即可完成高亮export function MyAppNav() { return ( nav NavLink to/ end Home /NavLink NavLink to/trending end Trending Concerts /NavLink NavLink to/concertsAll Concerts/NavLink NavLink to/accountAccount/NavLink /nav ); }a.active { color: red; }className、style、children三种 props 都支持接收以 active 状态为参数的函数便于做内联样式或条件渲染// className NavLink to/messages className{({ isActive }) isActive ? text-red-500 : text-black } Messages /NavLink// style NavLink to/messages style{({ isActive }) ({ color: isActive ? red : black, })} Messages /NavLink// children NavLink to/message {({ isActive }) ( span className{isActive ? active : } {isActive ? : } Tasks /span )} /NavLinkLink无 active 需求的普通链接不需要 active 样式时直接使用Linkimport { Link } from react-router; export function LoggedOutMessage() { return ( p Youve been logged out.{ } Link to/loginLogin again/Link /p ); }值得说明的是Link的点击拦截逻辑在仓库中由 DOM 层的useLinkClickHandler承担lib/dom/lib.tsx#L2211它把内部导航统一走路由而不是触发浏览器整页刷新——这正是不要用裸a这条规则的底层原因。组件BrowserRouter/HashRouter之所以同样位于该文件也是因为它们与 DOM 的 history 深度绑定。useNavigate用户不在场时的命令式导航普通场景优先Link/NavLink因为它们默认带来更好的体验键盘事件、无障碍标签、在新窗口打开、右键菜单等。useNavigate应保留给用户没有直接点击却需要跳转的场景例如表单提交完成后跳转闲置超时后登出计时类 UI测验倒计时等。import { useNavigate } from react-router; export function LoginPage() { let navigate useNavigate(); return ( MyHeader / MyLoginForm onSuccess{() { navigate(/dashboard); }} / MyFooter / / ); }URL 值params、Search Params 与 Location修改参数、search params 或 location state 之前文档要求先通读 docs/start/declarative/url-values.md 以及 docs/explanation/location.md。核心规则如下动态路由参数用useParams查询字符串状态用useSearchParams当前 location 对象与导航状态用useLocationURL 参数都要校验与解析——它们本质是字符串且可能缺失除非刻意清空否则不要丢弃无关的 search params。Route ParamsuseParams返回动态段解析出的值。例如Route path/concerts/:city中:city的值可通过useParams获取import { useParams } from react-router; function City() { let { city } useParams(); let data useFakeDataLibrary(/api/v2/cities/${city}); // ... }在源码层面useParams实现在 lib/hooks.tsx#L666它从 Router Context 中读取当前匹配的路由层级并汇总参数同文件还提供 useLocationL159 与 useRoutesL755其核心逻辑在useRoutesImplL763。行为验证可参考 packages/react-router/tests/useLocation-test.tsx 与 useParams-test.tsx 等测试。URL Search Params?之后的部分属于 search paramsuseSearchParams返回标准的URLSearchParams实例function SearchResults() { let [searchParams] useSearchParams(); return ( div p You searched for i{searchParams.get(q)}/i /p FakeSearchResults / /div ); }仓库实现位于 lib/dom/lib.tsx#L2383。注意两条实战纪律其一searchParams.get(q)可能返回null查询串里没有该键或没带必须处理缺失其二如果只是追加/替换某个键应当在原有searchParams上拷贝修改避免把用户可能并存的排序、过滤参数一起冲掉。Location ObjectReact Router 会构建一个自定义的location对象可通过useLocation读取其上带有 pathname、search、hash、key 等有用信息function useAnalytics() { let location useLocation(); useEffect(() { sendFakeAnalytics(location.pathname); }, [location]); } function useScrollRestoration() { let location useLocation(); useEffect(() { fakeRestoreScroll(location.key); }, [location]); }location.key在每次导航时都会变化因此常被用于按导航触发副作用如发送页面 PV、恢复滚动位置的依赖项。由于这些值都是从 URL/历史状态解码而来业务侧需要自行完成类型转换与边界校验。模式边界Declarative 里没有这些 APIDeclarative Mode 的边界是明确的它不提供Data/Framework 相关的整套 API包括loaderactionFormuseFetcheruseNavigationroute module exports如Route.ComponentProps、types等框架级产物生成的./types路由类型docs/start/modes.md 中有一张API × Mode 可用性总表。筛选出Declarative 列可用的能力如下也是你在这个模式里能依赖的全部武器APIFrameworkDataDeclarativeLink✅✅✅NavLink✅✅✅Navigate✅✅✅Outlet✅✅✅Route✅✅✅Routes✅✅✅useBeforeUnload✅✅✅useHref✅✅✅useInRouterContext✅✅✅useLinkClickHandler✅✅✅useLocation✅✅✅useMatch✅✅✅useNavigate✅✅✅useNavigationType✅✅✅useOutlet✅✅✅useOutletContext✅✅✅useParams✅✅✅useResolvedPath✅✅✅useRoutes✅✅✅useSearchParams✅✅✅createPath / createSearchParams / generatePath✅✅✅matchPath / matchRoutes / parsePath / renderMatches / resolvePath✅✅✅createRoutesFromElements只在 Data 列可用是数据路由把 JSXRoute转换为路由对象时的桥梁。对应地useNavigation、useLoaderData、useActionData、useRevalidator、useFetcher(s)、useMatches、Await、Form、useBlocker、ScrollRestoration等在这一列均为空——它们是升级到 Data Mode 之后才会解锁的。什么时候该升级到 Data / Framework Mode参考文档给出明确的建议如果用户提出以下诉求就应该推荐 Data Mode 或 Framework Mode具体选哪个取决于对方想要多少脚手架与约定路由数据加载route data loading依赖数据库/后端 API 的数据CRUD表单变更mutations需要从提交回传的校验错误validation returned from submissions重新校验revalidationpending UIoptimistic UIfetchers。迁移属于架构决策应先征求用户意见再动手除非对方已经主动提出迁移要求——本模式边界正是为了避免无意识地给声明式应用强行嫁接 data 能力。文档使用提示注意[MODES: ...]标记当前仓库的文档体系是多模式共存的一份*.md可能同时适用于多种模式。因此在查阅 docs 时请先确认文件头部的[MODES: framework, data, declarative]标记只有包含declarative的文档才适用于声明式应用。最稳妥的阅读路径是docs/start/modes.md # 先决定模式 docs/start/declarative/index.md # 声明式入口 docs/start/declarative/installation.md docs/start/declarative/routing.md docs/start/declarative/navigating.md docs/start/declarative/url-values.md概念性背景则参考 docs/explanation 下的对应主题。按这条路径阅读既能保证你获得的知识与当前代码版本一致也能避免把 Data/Framework 专有能力误用到声明式应用中。小结Declarative Mode 是 React Router 中只做路由该做的事的极简模式以BrowserRouter/HashRouter/MemoryRouter为根dom/lib.tsx以Routes/Route描述 URL 与组件的映射components.tsx通过Link/NavLink/useNavigate完成导航用useParams/useSearchParams/useLocation消费 URL 值hooks.tsx、dom/lib.tsx。它没有 loader/action/fetcher也没有 contenteditable="false">【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考