{"id":1921,"date":"2025-09-26T17:31:19","date_gmt":"2025-09-26T09:31:19","guid":{"rendered":"https:\/\/womeifei.cn\/?p=1921"},"modified":"2025-09-26T17:52:29","modified_gmt":"2025-09-26T09:52:29","slug":"react-router","status":"publish","type":"post","link":"https:\/\/womeifei.cn\/index.php\/2025\/09\/26\/react-router\/","title":{"rendered":"React-Router"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">\u2160.\u57fa\u7840\u8def\u7531\u914d\u7f6e<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>import { createBrowserRouter, RouterProvider } from 'react-router-dom';\n\n\/\/ 1. \u5b9a\u4e49\u8def\u7531\u914d\u7f6e\nconst router = createBrowserRouter(&#91;\n  {\n    path: '\/',\n    element: &lt;HomePage \/&gt;,\n  },\n  {\n    path: '\/about',\n    element: &lt;AboutPage \/&gt;,\n  },\n]);\n\n\/\/ 2. \u6302\u8f7d\u8def\u7531\nReactDOM.createRoot(root).render(\n  &lt;RouterProvider router={router} \/&gt;\n);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u2161.\u5d4c\u5957\u8def\u7531<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.\u57fa\u7840\u5d4c\u5957\u7ed3\u6784<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u901a\u8fc7&lt;Outlet&gt;\u5b9e\u73b0\u5e03\u5c40\u5d4c\u5957<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const router = createBrowserRouter(&#91;\n  {\n    path: '\/',\n    element: &lt;Layout \/>, \/\/ \u7236\u7ea7\u5e03\u5c40\n    children: &#91;\n      {\n        index: true, \/\/ \u9ed8\u8ba4\u8def\u7531\n        element: &lt;Dashboard \/>,\n      },\n      {\n        path: 'profile',\n        element: &lt;Profile \/>,\n      },\n      {\n        path: 'settings',\n        element: &lt;Settings \/>,\n      },\n    ],\n  },\n]);\n\n\/\/ Layout \u7ec4\u4ef6\u4f7f\u7528 Outlet\nfunction Layout() {\n  return (\n    &lt;div>\n      &lt;header>\u5bfc\u822a\u680f&lt;\/header>\n      &lt;main>\n        &lt;Outlet \/> {\/* \u5b50\u8def\u7531\u6e32\u67d3\u4f4d\u7f6e *\/}\n      &lt;\/main>\n      &lt;footer>\u9875\u811a&lt;\/footer>\n    &lt;\/div>\n  );\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2.\u591a\u7ea7\u5d4c\u5957<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>const router = createBrowserRouter(&#91;\n  {\n    path: '\/admin',\n    element: &lt;AdminLayout \/>,\n    children: &#91;\n      {\n        path: 'users',\n        element: &lt;UserManagementLayout \/>,\n        children: &#91;\n          {\n            path: 'list',\n            element: &lt;UserList \/>,\n          },\n          {\n            path: 'create',\n            element: &lt;UserCreate \/>,\n          },\n        ],\n      },\n    ],\n  },\n]);<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u2162.\u52a8\u6001\u8def\u7531<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.\u5b9a\u4e49\u52a8\u6001\u53c2\u6570<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>const router = createBrowserRouter(&#91;\n  {\n    path: '\/users\/:userId',\n    element: &lt;UserDetail \/>,\n  },\n  {\n    path: '\/products\/:category\/:id',\n    element: &lt;ProductDetail \/>,\n  },\n]);\n\n\/\/ \u7ec4\u4ef6\u4e2d\u83b7\u53d6\u53c2\u6570\nimport { useParams } from 'react-router-dom';\n\nfunction UserDetail() {\n  const { userId } = useParams(); \/\/ { userId: \"123\" }\n  return &lt;div>\u7528\u6237ID: {userId}&lt;\/div>;\n}\n\nfunction ProductDetail() {\n  const { category, id } = useParams(); \/\/ { category: \"electronics\", id: \"456\" }\n  return &lt;div>\u5206\u7c7b: {category}, ID: {id}&lt;\/div>;\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2.\u53ef\u9009\u53c2\u6570\u548c\u901a\u914d\u7b26<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>const router = createBrowserRouter(&#91;\n  {\n    path: '\/docs\/:section?', \/\/ \u53ef\u9009\u53c2\u6570\n    element: &lt;Documentation \/>,\n  },\n  {\n    path: '\/files\/*', \/\/ \u901a\u914d\u7b26\u5339\u914d\u6240\u6709\u5b50\u8def\u5f84\n    element: &lt;FileBrowser \/>,\n  },\n]);<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u2163.\u7f16\u7a0b\u5f0f\u5bfc\u822a<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.\u4f7f\u7528useNavigate<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import { useNavigate } from 'react-router-dom';\n\nfunction NavigationExample() {\n  const navigate = useNavigate();\n  \n  const handleNavigation = () => {\n    \/\/ \u57fa\u7840\u5bfc\u822a\n    navigate('\/target-path');\n    \n    \/\/ \u5e26\u72b6\u6001\u5bfc\u822a\n    navigate('\/dashboard', {\n      state: { from: 'login' },\n      replace: true, \/\/ \u66ff\u6362\u5386\u53f2\u8bb0\u5f55\n    });\n    \n    \/\/ \u76f8\u5bf9\u8def\u5f84\u5bfc\u822a\n    navigate('..\/parent'); \/\/ \u4e0a\u4e00\u7ea7\n    navigate('.\/child');    \/\/ \u5f53\u524d\u8def\u5f84\u4e0b\u7684\u5b50\u8def\u5f84\n  };\n  \n  return (\n    &lt;div>\n      &lt;button onClick={() => navigate(-1)}>\u8fd4\u56de&lt;\/button>\n      &lt;button onClick={() => navigate(1)}>\u524d\u8fdb&lt;\/button>\n      &lt;button onClick={handleNavigation}>\u8df3\u8f6c&lt;\/button>\n    &lt;\/div>\n  );\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2.\u91cd\u5b9a\u5411\u7ec4\u4ef6<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import { Navigate } from 'react-router-dom';\n\nfunction ProtectedRoute({ isAuthenticated, children }) {\n  return isAuthenticated ? children : &lt;Navigate to=\"\/login\" replace \/>;\n}\n\n\/\/ \u4f7f\u7528\u793a\u4f8b\n&lt;Route \n  path=\"\/admin\" \n  element={\n    &lt;ProtectedRoute isAuthenticated={user.isAdmin}>\n      &lt;AdminPanel \/>\n    &lt;\/ProtectedRoute>\n  } \n\/><\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u2164.\u8def\u7531\u5b88\u536b<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.\u9ad8\u9636\u7ec4\u4ef6\u5f62\u5f0f<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import { Navigate, useLocation } from 'react-router-dom';\n\nfunction RequireAuth({ children, roles = &#91;] }) {\n  const auth = useAuth();\n  const location = useLocation();\n  \n  if (!auth.user) {\n    return &lt;Navigate to=\"\/login\" state={{ from: location }} replace \/>;\n  }\n  \n  if (roles.length > 0 &amp;&amp; !roles.includes(auth.user.role)) {\n    return &lt;Navigate to=\"\/unauthorized\" replace \/>;\n  }\n  \n  return children;\n}\n\n\/\/ \u4f7f\u7528\u793a\u4f8b\nconst router = createBrowserRouter(&#91;\n  {\n    path: '\/admin',\n    element: (\n      &lt;RequireAuth roles={&#91;'admin', 'superadmin']}>\n        &lt;AdminPanel \/>\n      &lt;\/RequireAuth>\n    ),\n  },\n]);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2.Loader\u62e6\u622a<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u6743\u9650\u6821\u9a8c Loader\nexport async function adminLoader({ request }) {\n  const user = await getCurrentUser();\n  \n  if (!user) {\n    throw redirect('\/login');\n  }\n  \n  if (user.role !== 'admin') {\n    throw redirect('\/unauthorized');\n  }\n  \n  return { user };\n}\n\n\/\/ \u8def\u7531\u914d\u7f6e\nconst router = createBrowserRouter(&#91;\n  {\n    path: '\/admin',\n    element: &lt;AdminPanel \/>,\n    loader: adminLoader, \/\/ \u8def\u7531\u7ea7\u522b\u6743\u9650\u63a7\u5236\n  },\n]);<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u2165.\u7279\u6b8a\u8def\u7531\u914d\u7f6e<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.404\u9875\u9762<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>const router = createBrowserRouter(&#91;\n  \/\/ ...\u5176\u4ed6\u8def\u7531\n  {\n    path: '*',\n    element: &lt;NotFoundPage \/&gt;,\n  },\n]);<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u2166.\u6570\u636e\u52a0\u8f7d<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.Loader\u6570\u636e\u9884\u52a0\u8f7d<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import { json } from 'react-router-dom';\n\n\/\/ \u7528\u6237\u8be6\u60c5\u9875 Loader\nexport async function userLoader({ params, request }) {\n  const userId = params.userId;\n  \n  try {\n    const user = await fetchUser(userId);\n    const posts = await fetchUserPosts(userId);\n    \n    return json({ user, posts });\n  } catch (error) {\n    throw new Response('\u7528\u6237\u4e0d\u5b58\u5728', { status: 404 });\n  }\n}\n\n\/\/ \u7ec4\u4ef6\u4e2d\u4f7f\u7528\u6570\u636e\nimport { useLoaderData } from 'react-router-dom';\n\nfunction UserProfile() {\n  const { user, posts } = useLoaderData();\n  \n  return (\n    &lt;div>\n      &lt;h1>{user.name}&lt;\/h1>\n      {posts.map(post => (\n        &lt;article key={post.id}>{post.title}&lt;\/article>\n      ))}\n    &lt;\/div>\n  );\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2.Action\u6570\u636e\u5904\u7406<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u8868\u5355\u63d0\u4ea4 Action\nexport async function userAction({ request, params }) {\n  const formData = await request.formData();\n  const intent = formData.get('intent');\n  \n  switch (intent) {\n    case 'update':\n      return updateUser(params.userId, formData);\n    case 'delete':\n      return deleteUser(params.userId);\n    default:\n      throw new Error('\u672a\u77e5\u64cd\u4f5c');\n  }\n}\n\n\/\/ \u8868\u5355\u7ec4\u4ef6\nfunction UserForm() {\n  return (\n    &lt;Form method=\"post\">\n      &lt;input type=\"text\" name=\"name\" \/>\n      &lt;button type=\"submit\" name=\"intent\" value=\"update\">\n        \u66f4\u65b0\n      &lt;\/button>\n      &lt;button type=\"submit\" name=\"intent\" value=\"delete\">\n        \u5220\u9664\n      &lt;\/button>\n    &lt;\/Form>\n  );\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u2167.\u9ad8\u7ea7\u7279\u6027<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.\u61d2\u52a0\u8f7d\u8def\u7531<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>const router = createBrowserRouter(&#91;\n  {\n    path: '\/dashboard',\n    lazy: () => import('.\/routes\/Dashboard'), \/\/ \u52a8\u6001\u5bfc\u5165\n  },\n  {\n    path: '\/admin',\n    lazy: async () => {\n      const { AdminLayout, adminLoader } = await import('.\/routes\/Admin');\n      return { \n        Component: AdminLayout, \n        loader: adminLoader \n      };\n    },\n  },\n]);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2.\u9519\u8bef\u8fb9\u754c\u5904\u7406<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u8def\u7531\u7ea7\u522b\u9519\u8bef\u5904\u7406\nconst router = createBrowserRouter(&#91;\n  {\n    path: '\/',\n    element: &lt;Root \/>,\n    errorElement: &lt;ErrorBoundary \/>, \/\/ \u9519\u8bef\u8fb9\u754c\u7ec4\u4ef6\n    children: &#91;\n      {\n        path: 'users\/:userId',\n        element: &lt;UserDetail \/>,\n        errorElement: &lt;UserError \/>, \/\/ \u5b50\u8def\u7531\u9519\u8bef\u5904\u7406\n      },\n    ],\n  },\n]);\n\n\/\/ \u9519\u8bef\u8fb9\u754c\u7ec4\u4ef6\nfunction ErrorBoundary() {\n  const error = useRouteError();\n  \n  return (\n    &lt;div>\n      &lt;h1>\u51fa\u9519\u4e86\uff01&lt;\/h1>\n      &lt;p>{error.statusText || error.message}&lt;\/p>\n    &lt;\/div>\n  );\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3.\u6eda\u52a8\u6062\u590d<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import { ScrollRestoration } from 'react-router-dom';\n\nfunction Root() {\n  return (\n    &lt;>\n      &lt;ScrollRestoration \/> {\/* \u81ea\u52a8\u6062\u590d\u6eda\u52a8\u4f4d\u7f6e *\/}\n      &lt;Outlet \/>\n    &lt;\/>\n  );\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\u2168.\u6700\u4f73\u5b9e\u8df5<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.\u9879\u76ee\u7ec4\u7ec7\u7ed3\u6784<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">src\/<br>\u251c\u2500\u2500 routes\/ # \u8def\u7531\u7ec4\u4ef6<br>\u2502 \u251c\u2500\u2500 index.tsx # \u9996\u9875<br>\u2502 \u251c\u2500\u2500 about\/<br>\u2502 \u2502 \u251c\u2500\u2500 index.tsx<br>\u2502 \u2502 \u2514\u2500\u2500 team.tsx<br>\u2502 \u2514\u2500\u2500 users\/<br>\u2502 \u251c\u2500\u2500 index.tsx<br>\u2502 \u251c\u2500\u2500 [id].tsx # \u52a8\u6001\u8def\u7531<br>\u2502 \u2514\u2500\u2500 create.tsx<br>\u251c\u2500\u2500 components\/ # \u5171\u4eab\u7ec4\u4ef6<br>\u251c\u2500\u2500 layouts\/ # \u5e03\u5c40\u7ec4\u4ef6<br>\u251c\u2500\u2500 router\/ # \u8def\u7531\u914d\u7f6e<br>\u2502 \u2514\u2500\u2500 index.ts<br>\u2514\u2500\u2500 main.tsx<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.\u7c7b\u578b\u5b89\u5168\u914d\u7f6e<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ router\/types.ts\nimport { RouteObject } from 'react-router-dom';\n\nexport interface AppRoute extends RouteObject {\n  meta?: {\n    title?: string;\n    requiresAuth?: boolean;\n    roles?: string&#91;];\n  };\n  children?: AppRoute&#91;];\n}\n\n\/\/ router\/config.ts\nexport const routes: AppRoute&#91;] = &#91;\n  {\n    path: '\/',\n    element: &lt;Home \/>,\n    meta: { title: '\u9996\u9875' },\n  },\n];<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3.\u6027\u80fd\u4f18\u5316\u6280\u5de7<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ 1. \u4ee3\u7801\u5206\u5272\nconst LazyComponent = lazy(() => import('.\/HeavyComponent'));\n\n\/\/ 2. \u9884\u52a0\u8f7d\n&lt;Link to=\"\/dashboard\" onMouseEnter={() => preloadDashboard()} \/>\n\n\/\/ 3. \u8bb0\u5fc6\u5316\u5bfc\u822a\nconst navigate = useNavigate();\nconst memoizedNavigate = useCallback((path: string) => {\n  navigate(path);\n}, &#91;navigate]);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u2160.\u57fa\u7840\u8def\u7531\u914d\u7f6e \u2161.\u5d4c\u5957\u8def\u7531 1.\u57fa\u7840\u5d4c\u5957\u7ed3\u6784 \u901a\u8fc7&lt;Outlet&gt;\u5b9e\u73b0\u5e03\u5c40\u5d4c\u5957 2.\u591a\u7ea7\u5d4c\u5957  [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[47,1],"tags":[],"class_list":["post-1921","post","type-post","status-publish","format-standard","hentry","category-react","category-1"],"_links":{"self":[{"href":"https:\/\/womeifei.cn\/index.php\/wp-json\/wp\/v2\/posts\/1921","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/womeifei.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/womeifei.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/womeifei.cn\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/womeifei.cn\/index.php\/wp-json\/wp\/v2\/comments?post=1921"}],"version-history":[{"count":4,"href":"https:\/\/womeifei.cn\/index.php\/wp-json\/wp\/v2\/posts\/1921\/revisions"}],"predecessor-version":[{"id":1927,"href":"https:\/\/womeifei.cn\/index.php\/wp-json\/wp\/v2\/posts\/1921\/revisions\/1927"}],"wp:attachment":[{"href":"https:\/\/womeifei.cn\/index.php\/wp-json\/wp\/v2\/media?parent=1921"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/womeifei.cn\/index.php\/wp-json\/wp\/v2\/categories?post=1921"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/womeifei.cn\/index.php\/wp-json\/wp\/v2\/tags?post=1921"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}