[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frouter":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frouter":516,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frouter":541},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":14,"pinned":15,"draft":15,"body":16,"seo":512,"stem":513,"id":514,"extension":515},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frouter","路由","React Router（v6+）是 React 生态中最常用的客户端路由库。见 React 基础。","技能小册",[12,13],"react","router","2023-09-06",false,{"type":17,"value":18,"toc":504},"minimark",[19,22,38,43,169,180,183,240,244,252,273,302,305,358,365,369,444,447,450,500],[20,21,8],"h1",{"id":8},[23,24,25,32,33,37],"p",{},[26,27,31],"a",{"href":28,"rel":29},"https:\u002F\u002Freactrouter.com\u002F",[30],"nofollow","React Router","（v6+）是 React 生态中最常用的客户端路由库。见 ",[26,34,36],{"href":35},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fbasics","React 基础","。",[39,40,42],"h2",{"id":41},"基本配置数据路由可选","基本配置（数据路由可选）",[44,45,50],"pre",{"className":46,"code":47,"language":48,"meta":49,"style":49},"language-tsx shiki shiki-themes material-theme-lighter github-light github-dark","import { createBrowserRouter, RouterProvider } from \"react-router-dom\";\nimport { Root } from \".\u002FRoot\";\nimport { Home } from \".\u002Fpages\u002FHome\";\nimport { User } from \".\u002Fpages\u002FUser\";\n\nconst router = createBrowserRouter([\n  {\n    path: \"\u002F\",\n    element: \u003CRoot \u002F>,\n    children: [\n      { index: true, element: \u003CHome \u002F> },\n      { path: \"users\u002F:userId\", element: \u003CUser \u002F> },\n    ],\n  },\n]);\n\nexport function App() {\n  return \u003CRouterProvider router={router} \u002F>;\n}\n","tsx","",[51,52,53,61,67,73,79,86,92,98,104,110,116,122,128,134,140,146,151,157,163],"code",{"__ignoreMap":49},[54,55,58],"span",{"class":56,"line":57},"line",1,[54,59,60],{},"import { createBrowserRouter, RouterProvider } from \"react-router-dom\";\n",[54,62,64],{"class":56,"line":63},2,[54,65,66],{},"import { Root } from \".\u002FRoot\";\n",[54,68,70],{"class":56,"line":69},3,[54,71,72],{},"import { Home } from \".\u002Fpages\u002FHome\";\n",[54,74,76],{"class":56,"line":75},4,[54,77,78],{},"import { User } from \".\u002Fpages\u002FUser\";\n",[54,80,82],{"class":56,"line":81},5,[54,83,85],{"emptyLinePlaceholder":84},true,"\n",[54,87,89],{"class":56,"line":88},6,[54,90,91],{},"const router = createBrowserRouter([\n",[54,93,95],{"class":56,"line":94},7,[54,96,97],{},"  {\n",[54,99,101],{"class":56,"line":100},8,[54,102,103],{},"    path: \"\u002F\",\n",[54,105,107],{"class":56,"line":106},9,[54,108,109],{},"    element: \u003CRoot \u002F>,\n",[54,111,113],{"class":56,"line":112},10,[54,114,115],{},"    children: [\n",[54,117,119],{"class":56,"line":118},11,[54,120,121],{},"      { index: true, element: \u003CHome \u002F> },\n",[54,123,125],{"class":56,"line":124},12,[54,126,127],{},"      { path: \"users\u002F:userId\", element: \u003CUser \u002F> },\n",[54,129,131],{"class":56,"line":130},13,[54,132,133],{},"    ],\n",[54,135,137],{"class":56,"line":136},14,[54,138,139],{},"  },\n",[54,141,143],{"class":56,"line":142},15,[54,144,145],{},"]);\n",[54,147,149],{"class":56,"line":148},16,[54,150,85],{"emptyLinePlaceholder":84},[54,152,154],{"class":56,"line":153},17,[54,155,156],{},"export function App() {\n",[54,158,160],{"class":56,"line":159},18,[54,161,162],{},"  return \u003CRouterProvider router={router} \u002F>;\n",[54,164,166],{"class":56,"line":165},19,[54,167,168],{},"}\n",[23,170,171,172,175,176,179],{},"在 ",[51,173,174],{},"Root"," 布局组件中使用 ",[51,177,178],{},"\u003COutlet \u002F>"," 渲染子路由。",[39,181,182],{"id":182},"声明式导航",[44,184,186],{"className":46,"code":185,"language":48,"meta":49,"style":49},"import { Link, NavLink, useNavigate, useParams } from \"react-router-dom\";\n\n\u002F\u002F 链接\n\u003CLink to=\"\u002Fusers\u002F1\">用户\u003C\u002FLink>\n\n\u002F\u002F 编程式导航\nconst navigate = useNavigate();\nnavigate(\"\u002Flogin\");\n\n\u002F\u002F 读取动态参数\nconst { userId } = useParams();\n",[51,187,188,193,197,202,207,211,216,221,226,230,235],{"__ignoreMap":49},[54,189,190],{"class":56,"line":57},[54,191,192],{},"import { Link, NavLink, useNavigate, useParams } from \"react-router-dom\";\n",[54,194,195],{"class":56,"line":63},[54,196,85],{"emptyLinePlaceholder":84},[54,198,199],{"class":56,"line":69},[54,200,201],{},"\u002F\u002F 链接\n",[54,203,204],{"class":56,"line":75},[54,205,206],{},"\u003CLink to=\"\u002Fusers\u002F1\">用户\u003C\u002FLink>\n",[54,208,209],{"class":56,"line":81},[54,210,85],{"emptyLinePlaceholder":84},[54,212,213],{"class":56,"line":88},[54,214,215],{},"\u002F\u002F 编程式导航\n",[54,217,218],{"class":56,"line":94},[54,219,220],{},"const navigate = useNavigate();\n",[54,222,223],{"class":56,"line":100},[54,224,225],{},"navigate(\"\u002Flogin\");\n",[54,227,228],{"class":56,"line":106},[54,229,85],{"emptyLinePlaceholder":84},[54,231,232],{"class":56,"line":112},[54,233,234],{},"\u002F\u002F 读取动态参数\n",[54,236,237],{"class":56,"line":118},[54,238,239],{},"const { userId } = useParams();\n",[39,241,243],{"id":242},"路由守卫鉴权","路由守卫（鉴权）",[23,245,246,247,251],{},"React Router ",[248,249,250],"strong",{},"无内置","全局守卫，常见做法：",[253,254,255,263],"ol",{},[256,257,258,259,262],"li",{},"在布局路由的 ",[51,260,261],{},"loader"," 中检查会话并重定向（数据路由 API）。",[256,264,265,266,269,270,37],{},"包装 ",[51,267,268],{},"\u003CProtectedRoute>"," 组件：未登录则 ",[51,271,272],{},"\u003CNavigate to=\"\u002Flogin\" replace \u002F>",[44,274,276],{"className":46,"code":275,"language":48,"meta":49,"style":49},"function ProtectedRoute({ children }: { children: React.ReactNode }) {\n  const isAuth = useAuth();\n  if (!isAuth) return \u003CNavigate to=\"\u002Flogin\" replace \u002F>;\n  return \u003C>{children}\u003C\u002F>;\n}\n",[51,277,278,283,288,293,298],{"__ignoreMap":49},[54,279,280],{"class":56,"line":57},[54,281,282],{},"function ProtectedRoute({ children }: { children: React.ReactNode }) {\n",[54,284,285],{"class":56,"line":63},[54,286,287],{},"  const isAuth = useAuth();\n",[54,289,290],{"class":56,"line":69},[54,291,292],{},"  if (!isAuth) return \u003CNavigate to=\"\u002Flogin\" replace \u002F>;\n",[54,294,295],{"class":56,"line":75},[54,296,297],{},"  return \u003C>{children}\u003C\u002F>;\n",[54,299,300],{"class":56,"line":81},[54,301,168],{},[39,303,304],{"id":304},"懒加载",[44,306,308],{"className":46,"code":307,"language":48,"meta":49,"style":49},"const Dashboard = lazy(() => import(\".\u002Fpages\u002FDashboard\"));\n\n{\n  path: \"dashboard\",\n  element: (\n    \u003CSuspense fallback={\u003Cp>加载中…\u003C\u002Fp>}>\n      \u003CDashboard \u002F>\n    \u003C\u002FSuspense>\n  ),\n}\n",[51,309,310,315,319,324,329,334,339,344,349,354],{"__ignoreMap":49},[54,311,312],{"class":56,"line":57},[54,313,314],{},"const Dashboard = lazy(() => import(\".\u002Fpages\u002FDashboard\"));\n",[54,316,317],{"class":56,"line":63},[54,318,85],{"emptyLinePlaceholder":84},[54,320,321],{"class":56,"line":69},[54,322,323],{},"{\n",[54,325,326],{"class":56,"line":75},[54,327,328],{},"  path: \"dashboard\",\n",[54,330,331],{"class":56,"line":81},[54,332,333],{},"  element: (\n",[54,335,336],{"class":56,"line":88},[54,337,338],{},"    \u003CSuspense fallback={\u003Cp>加载中…\u003C\u002Fp>}>\n",[54,340,341],{"class":56,"line":94},[54,342,343],{},"      \u003CDashboard \u002F>\n",[54,345,346],{"class":56,"line":100},[54,347,348],{},"    \u003C\u002FSuspense>\n",[54,350,351],{"class":56,"line":106},[54,352,353],{},"  ),\n",[54,355,356],{"class":56,"line":112},[54,357,168],{},[23,359,360,361,37],{},"详见 ",[26,362,364],{"href":363},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fbuild","performance\u002Fbuild",[39,366,368],{"id":367},"与-vue-router-对照","与 Vue Router 对照",[370,371,372,387],"table",{},[373,374,375],"thead",{},[376,377,378,381,384],"tr",{},[379,380],"th",{},[379,382,383],{},"React Router 6",[379,385,386],{},"Vue Router 4",[388,389,390,408,422],"tbody",{},[376,391,392,396,402],{},[393,394,395],"td",{},"配置",[393,397,398,399],{},"JSX 路由对象或 ",[51,400,401],{},"createBrowserRouter",[393,403,404,407],{},[51,405,406],{},"routes"," 数组",[376,409,410,413,417],{},[393,411,412],{},"出口",[393,414,415],{},[51,416,178],{},[393,418,419],{},[51,420,421],{},"\u003CRouterView \u002F>",[376,423,424,427,436],{},[393,425,426],{},"导航",[393,428,429,432,433],{},[51,430,431],{},"useNavigate"," \u002F ",[51,434,435],{},"\u003CLink>",[393,437,438,432,441],{},[51,439,440],{},"useRouter",[51,442,443],{},"\u003CRouterLink>",[39,445,446],{"id":446},"参考文献",[23,448,449],{},"以下链接在编写时均可正常访问：",[370,451,452,462],{},[373,453,454],{},[376,455,456,459],{},[379,457,458],{},"资料",[379,460,461],{},"说明",[388,463,464,476,488],{},[376,465,466,473],{},[393,467,468],{},[26,469,472],{"href":470,"rel":471},"https:\u002F\u002Freactrouter.com\u002Fen\u002Fmain",[30],"React Router 文档",[393,474,475],{},"英文官方",[376,477,478,485],{},[393,479,480],{},[26,481,484],{"href":482,"rel":483},"https:\u002F\u002Freactrouter.com\u002Fen\u002Fmain\u002Fstart\u002Ftutorial",[30],"React Router：Tutorial",[393,486,487],{},"入门",[376,489,490,497],{},[393,491,492],{},[26,493,496],{"href":494,"rel":495},"https:\u002F\u002Fzh-hans.react.dev\u002Freference\u002Freact\u002Flazy",[30],"React：lazy",[393,498,499],{},"懒加载组件",[501,502,503],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":49,"searchDepth":63,"depth":63,"links":505},[506,507,508,509,510,511],{"id":41,"depth":63,"text":42},{"id":182,"depth":63,"text":182},{"id":242,"depth":63,"text":243},{"id":304,"depth":63,"text":304},{"id":367,"depth":63,"text":368},{"id":446,"depth":63,"text":446},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FReact\u002Frouter","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FReact\u002Frouter.md","md",{"items":517},[518,522,526,530,534,537],{"path":519,"title":520,"description":521,"category":10,"score":88},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frendering","渲染与协调","React 将组件渲染为虚拟 DOM 树，再协调（reconciliation）到浏览器 DOM。理解该过程有助于解释 key、memo 与性能优化。涵盖渲染流程、Fiber、startTransition、Suspense、最佳实践与常见坑。",{"path":523,"title":524,"description":525,"category":10,"score":88},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fproject","工程化","新建 React 项目时，官方与社区主流工具为 Vite；全栈场景常用 Next.js。涵盖 Vite + React、Create React App 现状、Next.js、目录结构、环境变量、部署、最佳实践与常见坑。",{"path":527,"title":528,"description":529,"category":10,"score":88},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Ftypescript","TypeScript","React 官方推荐 TypeScript。类型主要落在 Props、事件、Ref 与 Hooks 返回值上。见 组件。",{"path":531,"title":532,"description":533,"category":10,"score":88},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Ftest","测试","React 组件测试推荐 React Testing Library（RTL）：从用户可见行为断言，而非实现细节。运行器常用 Vitest 或 Jest。涵盖安装配置、查询优先级、异步测试、Mock 网络请求、最佳实践与常见坑。",{"path":363,"title":535,"description":536,"category":10,"score":88},"构建与分包","通过代码分割与构建配置减小首屏 JavaScript 体积。涵盖 React.lazy\u002FSuspense、路由级分割、Tree Shaking、Vite 生产构建、体积分析、最佳实践与常见坑。运行时优化见 runtime。",{"path":538,"title":539,"description":540,"category":10,"score":88},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Feffect","Effect 与副作用","useEffect 用于在组件渲染后执行与外部系统同步的逻辑（请求、订阅、手动改 DOM 等）。涵盖基本用法、依赖数组、适用\u002F不适用场景、useLayoutEffect、最佳实践与常见坑。",{"series":542,"title":12,"items":543,"index":112,"prev":591,"next":592},"booklet\u002Fframe\u002Freact",[544,546,548,552,556,560,562,566,570,572,574,575,579,583,587,589],{"path":35,"title":36,"date":545,"pinned":15},"2020-02-12",{"path":363,"title":535,"date":547,"pinned":15},"2025-07-23",{"path":549,"title":550,"date":551,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fcomponents","组件与 Props","2020-06-11",{"path":553,"title":554,"date":555,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fcontext","Context","2021-10-16",{"path":557,"title":558,"date":559,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fecology","生态","2023-02-04",{"path":538,"title":539,"date":561,"pinned":15},"2024-10-11",{"path":563,"title":564,"date":565,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fjsx","JSX 与语法","2020-02-29",{"path":567,"title":568,"date":569,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fmemo","Memo、Callback 与 Ref","2021-02-28",{"path":523,"title":524,"date":571,"pinned":15},"2026-05-08",{"path":519,"title":520,"date":573,"pinned":15},"2026-07-10",{"path":7,"title":8,"date":14,"pinned":15},{"path":576,"title":577,"date":578,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fperformance\u002Fruntime","运行时优化","2020-02-05",{"path":580,"title":581,"date":582,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fstate","State 与 Reducer","2021-02-21",{"path":584,"title":585,"date":586,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fstate","状态管理","2024-02-07",{"path":531,"title":532,"date":588,"pinned":15},"2025-09-26",{"path":527,"title":528,"date":590,"pinned":15},"2026-03-13",{"path":519,"title":520,"date":573,"pinned":15},{"path":576,"title":577,"date":578,"pinned":15}]