
React Router V6 重定向指南深入理解 Navigate 组件与 Refine 中的实际应用【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refineNavigate是 React Router V6 内置的声明式重定向组件是useNavigateHook 的组件化等价物通过渲染组件即可触发路由跳转。本文以该组件为主线先讲解在 React 项目中安装、配置 React Router V6 并完成基础路由搭建再深入剖析Navigate的三个 props 及登录成功后跳转的完整示例随后结合 Refine 仓库中refinedev/react-router包的源码介绍NavigateToResource、CatchAllNavigate等基于Navigate扩展的实战组件最后覆盖懒加载、代码分割、性能优化与路由测试等配套最佳实践。React Router V6 起步安装 React Router在已有 React 项目中根据你使用的包管理器从 NPM 仓库安装 React Router V6# 使用 NPM npm install react-router-dom6 # 使用 Yarn yarn add react-router-dom6 # 使用 pnpm pnpm add react-router-dom6配置 BrowserRouter在浏览器环境中使用 React Router 之前需要用它的顶层组件包裹根组件。BrowserRouter是浏览器环境下官方推荐的方式它利用 History API 让 UI 与 URL 保持同步import React from react; import ReactDOM from react-dom/client; import { BrowserRouter as Router } from react-router-dom; import App from ./App; const root ReactDOM.createRoot(document.getElementById(root)); root.render( React.StrictMode Router App / /Router /React.StrictMode, );设置路由React Router V6 中使用Routes与Route两个组件声明路由。Routes会遍历所有Route仅渲染与当前 URL 匹配的那一个import { Route, Routes } from react-router-dom; import { Home, Blog } from ./components; function App() { return ( div classNameApp Routes Route path/ element{Home /} / Route path/blog element{Blog /} / /Routes /div ); }如何使用 Navigate 组件Navigate是 React Router V6 提供的若干内置组件之一它内部封装了useNavigateHook。只要组件被渲染render当前 location 就会立即发生改变因此它非常适合用于“条件渲染式”的重定向场景。import { Navigate } from react-router-dom;Navigate最多接收三个 props其中只有一个必填其余两个可选Navigate to/dashboard state{{ todos: [] }} replace{true} /to—— 必填 prop值为要跳转的目标路径。replace—— 可选的布尔 prop。设为true时将替换replace历史栈中当前的记录而不是压入新记录。state—— 可选 prop用于把数据从渲染Navigate的组件传递给目标路由。下面是一个完整示例登录表单提交成功后更新 state再条件渲染Navigate完成跳转并通过stateprop 把用户对象传给目标页面const App () { const [loginDetails, setLoginDetails] useState({ email: , password: }); const [user, setUser] useState(null); const changeHandler (e) { const { name, value } e.target; setLoginDetails((loginDetails) ({ ...loginDetails, [name]: value })); }; const submitHandler async (e) { e.preventDefault(); const user await loginUser(loginDetails); setUser(user); }; return ( div form onSubmit{submitHandler} label Email: input typeemail nameemail value{loginDetails.email} onChange{changeHandler} required / /label label Password: input typepassword namepassword value{loginDetails.password} onChange{changeHandler} required / /label button typesubmit Login /button /form {user Navigate to/dashboard state{user} replace{true} /} /div ); };跳转完成后匹配路由渲染的组件可以通过useLocationHook 读取stateprop 传递的数据const location useLocation(); console.log(location.state); // 未传递任何数据时location.state 的默认值为 null需要注意传给Navigate的 props与useNavigateHook 返回的函数所要求的参数完全一致。在 React 中函数式组件可以使用 Hooks但 ES6 类组件不支持 Hooks因此当你需要在类组件中实现重定向时Navigate就是useNavigate的便捷替代方案。在 Refine 中基于 Navigate 的实战扩展在 Refine 项目中路由体系建立在 React Router 之上refinedev/react-router包提供了将 Refine 与 React Router 深度集成的routerProvider以及若干基于Navigate扩展的组件。其实现位于 packages/react-router/src下面逐一剖析。NavigateToResource跳转到资源页面NavigateToResource是Navigate的扩展组件常用于应用根路径index route自动重定向到某个资源的列表页。源码见 packages/react-router/src/navigate-to-resource.tsxexport const NavigateToResource: React.FCNavigateToResourceProps ({ resource: resourceProp, fallbackTo, meta, }) { const getToPath useGetToPath(); const { resource, resources } useResourceParams({ resource: resourceProp, }); const toResource resource || resources.find((r) r.list); if (toResource) { const path getToPath({ resource: toResource, action: list, meta, }); if (path) { return Navigate to{path} /; } return null; } if (fallbackTo) { console.warn(No resource is found. navigation to ${fallbackTo}.); return Navigate to{fallbackTo} /; } console.warn( No resource and fallbackTo is found. No navigation will be made., ); return null; };从源码可以看到其三个可选 props 的行为resource可选——要跳转到的资源名称如果不传会重定向到resources数组中第一个定义了list路由的资源。fallbackTo可选——当找不到任何资源时跳转的路径如果未提供且没有找到资源则不会发生任何跳转并输出 console 警告。meta可选——当目标路由包含参数时使用的 meta 对象当前 location 中的参数也会参与路由组合但meta优先级更高。对应测试用例见 packages/react-router/src/navigate-to-resource.test.tsx其中通过 mockNavigate组件捕获to值验证了“跳转到第一个带 list 的资源/posts”“跳转到指定资源/categories”“无资源时跳转 fallbackTo”等行为。实际使用方式如下把根路径/的路由元素设为NavigateToResource用户访问应用根路径时即被重定向到/postsimport routerProvider, { NavigateToResource } from refinedev/react-router; const App () { return ( BrowserRouter Refine routerProvider{routerProvider} resources{[ { name: posts, list: /posts, }, ]} Routes Route path/ element{NavigateToResource resourceposts /} / Route path/posts element{PostList /} / /Routes /Refine /BrowserRouter ); };CatchAllNavigate带回调地址的重定向CatchAllNavigate用于处理 404 页面与认证结合的场景它会跳转到指定路径并把当前 location 保存在to查询参数中以便登录后跳回原页面。实现见 packages/react-router/src/catch-all-navigate.tsxexport const CatchAllNavigate: React.FC{ to: string } ({ to }) { const { pathname, search } useLocation(); const queryValue ${pathname}${search}; const query queryValue.length 1 ? ?to${encodeURIComponent(queryValue)} : ; return Navigate to{${to}${query}} /; };其唯一必填 prop 是to要跳转的路径。例如catch-all路由中把它指向登录页Route path* element{CatchAllNavigate to/login /} /当用户访问未定义的路由时会被重定向到/login?to%2Fposts登录成功后即可依据to参数回到原目标页。routerProvider 的 go 方法Navigate 背后的导航能力Navigate属于 React Router 自带能力而 Refine 的routerProvider在此基础上进一步封装。在 packages/react-router/src/bindings.tsx 中go方法内部调用了useNavigate并支持to、typepath/push/replace、query、hash以及keepQuery、keepHash选项最终通过navigate(fullPath, { replace: type replace })完成跳转parse方法则会解析当前 URL 中的资源、action、id 与查询参数。这也是理解 Refine 路由体系的关键入口更多说明可参考 documentation/docs/routing/integrations/react-router/index.md 与 documentation/docs/routing/router-provider/index.md。如果你的业务需要更精细的导航控制Refine 还提供了 useNavigation 与useGo等 Hook前者为 legacy API官方建议新代码优先使用useGo/useGetToPath。React Router V6 的进阶特性懒加载路由Lazy Loading懒加载能够按需加载组件避免一次性加载全部页面代码从而显著缩短首屏加载时间import React, { Suspense, lazy } from react; import { BrowserRouter as Router, Routes, Route } from react-router-dom; // 懒加载组件 const Home lazy(() import(./components/Home)); const Blog lazy(() import(./components/Blog)); function App() { return ( Router Suspense fallback{divLoading.../div} Routes Route path/ element{Home /} / Route path/blog element{Blog /} / /Routes /Suspense /Router ); } export default App;上例中Home和Blog仅在用户导航到对应路由时才被加载Suspense起到占位符或 fallback UI如 loading 指示器的作用直到组件加载完成。代码分割Code Splitting代码分割把应用代码拆分为多个小 bundle按需加载而不是一次性下载整个巨型文件。结合 React Router V6 的典型做法import React, { Suspense, lazy } from react; import { BrowserRouter as Router, Routes, Route } from react-router-dom; // 懒加载组件 const Home lazy(() import(./components/Home)); const Blog lazy(() import(./components/Blog)); const Contact lazy(() import(./components/Contact)); function App() { return ( Router Suspense fallback{divLoading.../div} Routes Route path/ element{Home /} / Route path/blog element{Blog /} / Route path/contact element{Contact /} / /Routes /Suspense /Router ); } export default App;Home、Blog、Contact被拆分为独立 bundle仅在用户导航到对应路由时加载从而把首屏需要下载的代码量降到最低。性能优化使用 Memoization 减少重渲染React.memo可以缓存组件、避免不必要的重复渲染仅在 props 变化时才重新渲染import React from react; import { BrowserRouter as Router, Routes, Route } from react-router-dom; const Home React.memo(() { console.log(Home component rendered); return h1Home Page/h1; }); const Blog React.memo(() { console.log(Blog component rendered); return h1Blog Page/h1; }); function App() { return ( Router Routes Route path/ element{Home /} / Route path/blog element{Blog /} / /Routes /Router ); } export default App;优化路由匹配Routes组件一次只会匹配并渲染一个路由这本身就是一种性能优化。在此基础上把不常访问的大型路由改为懒加载可进一步提升性能import React, { Suspense, lazy } from react; import { BrowserRouter as Router, Routes, Route } from react-router-dom; const Home React.memo(() h1Home Page/h1); const Blog React.memo(() h1Blog Page/h1); const About lazy(() import(./About)); function App() { return ( Router Suspense fallback{divLoading.../div} Routes Route path/ element{Home /} / Route path/blog element{Blog /} / Route path/about element{About /} / /Routes /Suspense /Router ); } export default App;此处About被懒加载只有用户访问/about时才加载有效降低了初始加载时间。将 memoization 与优化的路由匹配结合可以显著改善应用性能与用户体验。React Router 测试路由单元测试可以用 Jest 配合 React Testing Library 对路由组件做单元测试验证每个组件在其路由下是否被正确渲染import React from react; import { render } from testing-library/react; import { BrowserRouter as Router, Routes, Route } from react-router-dom; import testing-library/jest-dom/extend-expect; const Home () h1Home Page/h1; const Blog () h1Blog Page/h1; test(should render Home component for / route, () { const { getByText } render( Router Routes Route path/ element{Home /} / Route path/blog element{Blog /} / /Routes /Router, ); expect(getByText(Home Page)).toBeInTheDocument(); }); test(renders Blog component for /blog route, () { window.history.pushState({}, Blog Page, /blog); const { getByText } render( Router Routes Route path/ element{Home /} / Route path/blog element{Blog /} / /Routes /Router, ); expect(getByText(Blog Page)).toBeInTheDocument(); });第一个测试渲染默认路径并断言 Home 页面存在第二个测试先用window.history.pushState把 URL 改到/blog再断言 Blog 页面被渲染。导航集成测试集成测试会模拟用户从应用入口一路操作到最终状态的完整流程包括页面间的导航行为import React from react; import { render, fireEvent } from testing-library/react; import { BrowserRouter as Router, Routes, Route, Link } from react-router-dom; import testing-library/jest-dom/extend-expect; const Home () ( div h1Home Page/h1 Link to/blogGo to Blog/Link /div ); const Blog () ( div h1Blog Page/h1 Link to/Go to Home/Link /div ); test(Goes from Home to Blog and back, async () { const { getByText } render( Router Routes Route path/ element{Home /} / Route path/blog element{Blog /} / /Routes /Router, ); // 验证基础路由 expect(getByText(Home Page)).toBeInTheDocument(); // 跳转到 Blog fireEvent.click(getByText(Go to Blog)); expect(getByText(Blog Page)).toBeInTheDocument(); // 回到 Home fireEvent.click(getByText(Go to Home)); expect(getByText(Home Page)).toBeInTheDocument(); });这里使用fireEvent模拟用户点击链接并在每次导航后断言正确的组件被渲染。单元测试与集成测试相结合可以确保 React Router 应用在真实用户交互下行为符合预期。总结Navigate是 React Router V6 内置的声明式重定向组件是useNavigateHook 的组件等价物内部即基于useNavigate实现传给Navigate的 props 与传给useNavigate返回函数的参数完全一致。由于 React 的 ES6 类组件不支持 HooksNavigate是在类组件场景下替代useNavigate的便捷方案。在实际的 Refine 项目中refinedev/react-router包在 React Router 之上进一步封装了routerProvider并提供了NavigateToResource、CatchAllNavigate等基于Navigate的实用组件用于资源列表页跳转与带回调地址的认证重定向再配合懒加载、代码分割、memoization 优化与路由测试即可构建出结构清晰、性能良好且易于维护的 React Router 应用。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考