Stories

Detail Return Return

React Router V6項目中的路由鑑權封裝實踐(Hooks) - Stories Detail

React Router V6項目中的路由鑑權封裝實踐(Hooks)

1. 前言

1.1 路由封裝的好處

  • 路由鑑權集中管理: 封裝路由組件允許你集中管理路由鑑權邏輯。這意味着在一個地方處理用户是否有權限訪問某個路由,而不是在每個頁面或組件中重複相同的鑑權邏輯。這有助於保持一致性,並簡化了對路由鑑權的維護和更新。
  • 提高代碼複用性: 封裝路由組件可以促進代碼的複用。你可以將通用的路由配置、鑑權邏輯或其他功能抽象為可複用的組件,以便在整個應用程序中多次使用。這降低了重複編寫相似代碼的需求,提高了代碼複用性。
  • 易於擴展: 當項目需求變化時,封裝的路由組件使得擴展和調整路由配置變得更加容易。你可以輕鬆地添加新的路由或更改現有路由的配置,而不會影響到整個應用程序的其他部分。
  • 更清晰的項目結構: 路由組件的再封裝可以幫助建立清晰的項目結構。通過將路由相關的代碼放在專用的文件或文件夾中,項目的結構更容易理解和導航,減少了代碼文件的混雜性。

1.2 整體項目結構

 - src
   - layout
       - index.ts  # UI主框架(鑑權之後才能進的)
   - tools
       - auth.ts   # 權限相關工具文件
   - router
       - router.tsx  # 路由組件註冊
       - routerMap.tsx  # 路由表構建
       - privateRouter.ts  # 權限路由組件
       - router.ts  # 路由組件註冊
   - pages #(下面都是隨便弄的,要對自己的需求)
       - community.tsx  # 社區壓面
       - login.tsx  # 登錄界面
       - user.ts  # 用户界面
       - book.ts  # 書籍列表界面

2. 前期準備工作

2.1 安裝依賴

  pnpm add antd --save # 因為是一個小案例,所以做了基礎的UI開發
  pnpm add react-router-dom --save #(現在默認是V6版本的路由)

2.2 編寫工具文件

 /**
  * 設置token
  * @param token
  * @returns
  */
 export const setToken = (token: string) =>
     window.localStorage.setItem("auth_token", token);
 /**
  * 獲取token
  * @returns
  */
 export const getToken = () => window.localStorage.getItem("auth_token");
 /**
  * 獲取token
  * @returns
  */
 export const clearToken = () => window.localStorage.removeItem("auth_token");
 ​

2.3 編寫具體頁面組件

僅僅以社區列表這個組件為例,其實就是每個具體頁面準備好
 import React from "react";
 ​
 export default function Community() {
     return <div>社區列表界面</div>;
 }
 ​

3. 路由組件的開發

3.1 配置項目路由的根組件

 import React from "react";
 import ReactDOM from "react-dom/client";
 import App from "./App.tsx";
 import { BrowserRouter } from "react-router-dom";
 ​
 ReactDOM.createRoot(document.getElementById("root")!).render(
     <React.StrictMode>
         <BrowserRouter>
             <App />
         </BrowserRouter>
     </React.StrictMode>
     //這裏配置的是BrowserRouter,根據需要,可選擇這個或者HashRouter,兩者差別這裏就略過了,可以看看router v6基礎篇或其他文章
 );
 ​

3.2 守衞路由的編寫

其實就是做了一個基本的鑑權與過期處理,自己項目如果有更多的需求,就在try裏面加就可以了
 import { message } from "antd";
 import { ReactElement, useEffect } from "react";
 import { getToken } from "../tools/auth";
 import { useNavigate } from "react-router-dom";
 ​
 interface Props {
     children: ReactElement;
 }
 const PrivateRoute = ({ children }: Props) => {
     const navigator = useNavigate();
 ​
     // 對比時間戳是否超過48小時
     function isPast48Hours(timestamp: number): boolean {
         // 獲取當前時間戳
         const currentTimestamp = Math.floor(Date.now() / 1000);
 ​
         // 計算時間差,單位為秒
         const timeDifference = currentTimestamp - timestamp;
 ​
         // 定義48小時的秒數
         const hours48InSeconds = 48 * 60 * 60;
 ​
         // 判斷時間差是否超過48小時
         return timeDifference > hours48InSeconds;
     }
 ​
     useEffect(() => {
         try {
             const token: any = getToken();
             const tokenObj = JSON.parse(token);
             if (tokenObj === null || isPast48Hours(tokenObj.expired)) {
                 message.warning("token過期,請重新登錄");
                 navigator(`/login`);
             }
         } catch (error) {
             message.warning("token過期,請重新登錄");
             navigator(`/login`);
         }
     }, []);
     return <>{children}</>;
 };
 ​
 export default PrivateRoute;
 ​

3.3 路由映射表的編寫

這裏沒有直接用<Route/>組件愛你包裹,而是先用js對象形式維護了一套路由表數據,方便其他諸如: 菜單/目錄等組件的複用
 import { Navigate } from "react-router-dom";
 import Login from "../pages/login";
 import User from "../pages/User";
 import Community from "../pages/Community";
 import Book from "../pages/Book";
 import Layout from "../layout";
 import PrivateRoute from "./privateRoute";
 export const routerMap = [
     {
         path: "/login",
         element: <Login />,
     },
     {
         path: "/",
         element: (
             <PrivateRoute>
                 <Layout />
             </PrivateRoute>
         ),
         children: [
             {
                 path: "/user",
                 element: <User />,
             },
             {
                 path: "/community",
                 element: <Community />,
             },
             {
                 path: "/book",
                 element: <Book />,
             },
         ],
     },
     {
         path: "*",
         element: <Navigate to="/login" />,
     }, //其他沒有被註冊過的路徑統一重定位到login
 ];
 ​

3.4 路由註冊的編寫

其實就是將原先的路由表數據註冊為路由組件
 ​
 import { useRoutes } from "react-router-dom";
 import { routerMap } from "./routerMap";
 ​
 function Router() {
     const routerTab = useRoutes(routerMap); //註冊前端路由表
 ​
     return <div>{routerTab}</div>;
 }
 ​
 export default Router;
 ​

3.5 路由渲染

 import Router from "./router/router";
 ​
 function App() {
     return (
         <>
             <Router />
         </>
     );
 }
 ​
 export default App;
 ​

4. 組件內應用

4.1 Layout組件應用測試

Layout佈局組件,一個簡單的小Demo來測試路由正確性,他會被權限組件<PrivateRoute/>包裹,受到保護
 import { Tabs, TabsProps } from "antd";
 import React from "react";
 import { Outlet, useNavigate } from "react-router-dom";
 import { clearToken } from "../tools/auth";
 export default function Layout() {
     const navigator = useNavigate();
     const items: TabsProps["items"] = [
         {
             key: "book",
             label: "書籍列表頁面",
         },
         {
             key: "community",
             label: "社區列表頁面",
         },
         {
             key: "user",
             label: "用户列表頁面",
         },
         {
             key: "login",
             label: "退出登錄",
         },
     ];
     const handleChangeRoute = (value: string) => {
         if (value === "login") {
             clearToken();
         }
         navigator(`/${value}`);
     };
     return (
         <>
             <div style={{ maxWidth: "500px", margin: "0 auto" }}>
                 <div
                     style={{
                         display: "flex",
                         alignItems: "center",
                         justifyContent: "center",
                     }}
                 >
                     <Tabs
                         defaultActiveKey="community"
                         size={"large"}
                         items={items}
                         onChange={(value) => {
                             handleChangeRoute(value);
                         }}
                     />
                 </div>
                 <div
                     style={{
                         marginTop: "50px",
                         display: "flex",
                         alignItems: "center",
                         justifyContent: "center",
                     }}
                 >
                     <Outlet />
                 </div>
             </div>
         </>
     );
 }
 ​

4.2 登錄組件的應用測試

Login登錄組件,一個簡單的小Demo來測試路由正確性,他不會被權限組件<PrivateRoute/>包裹,可以隨意進入
 import { Button } from "antd";
 import React from "react";
 import { setToken } from "../../tools/auth";
 import { useNavigate } from "react-router-dom";
 ​
 export default function Login() {
     const navigator = useNavigate();
     const handleLogin = () => {
         const preToken: object = {
             token: "hjsdbvfjhysebfjkd762354",
             expired: Date.now(),
         };
         console.log(JSON.stringify(preToken));
 ​
         setToken(JSON.stringify(preToken)); //模擬設置token
         navigator(`/`);
     };
 ​
     const handleRush = () => {
         navigator(`/`); //模擬強行進入主頁
     };
     return (
         <div
             style={{
                 marginTop: "30px",
                 display: "flex",
                 alignItems: "center",
                 justifyContent: "center",
             }}
         >
             <Button
                 type={"primary"}
                 onClick={() => {
                     handleLogin();
                 }}
                 style={{ marginRight: "15px" }}
             >
                 登錄進入系統主頁
             </Button>
             <Button
                 type={"primary"}
                 onClick={() => {
                     handleRush();
                 }}
             >
                 強行進入系統主頁
             </Button>
         </div>
     );
 }
 ​

5. 總結

本實踐沒有過多的文本描述,多在代碼中的註釋。但通過此個實踐瞭解學習之後,應該可以較好的掌握在的React Hooks項目中應用Router V6封裝整個項目的路由系統,能夠真正實現一次封裝,多處收益

相關的配套實踐Demo會上傳Github開源

項目鏈接:React Router V6項目中的路由鑑權封裝實踐(Hooks)

Add a new Comments

Some HTML is okay.