|
@@ -286,7 +286,179 @@ navigate({pathname: '/login'}, {replace: true, state: {}})
|
|
|
|
|
|
## 7. 页面访问权限
|
|
|
|
|
|
+> Vue Router中可以通过导航守卫钩子来拦截到所有的页面导航,根据当前用户权限决定是否继续导航。
|
|
|
+>
|
|
|
+> 但是React Router需要根据不同的路由定义方式来自行实现。
|
|
|
+
|
|
|
+### 7.1 自定义hook
|
|
|
+
|
|
|
+```js
|
|
|
+/**
|
|
|
+ *! 自定义hook
|
|
|
+ ** 1 hook 函数的名称 必须 以 'use'开头去命名
|
|
|
+ ** 2 自定义hook中可以任意使用内置的所有hook函数
|
|
|
+ */
|
|
|
+
|
|
|
+import { useEffect } from 'react';
|
|
|
+import { useLocation, useNavigate } from 'react-router-dom';
|
|
|
+const whiteList = ['/', '/about', '/login'];
|
|
|
+
|
|
|
+export function useAuth() {
|
|
|
+ let location = useLocation();
|
|
|
+ let navigate = useNavigate();
|
|
|
+ let token = localStorage.getItem('token');
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (!token && !whiteList.includes(location.pathname)) {
|
|
|
+ navigate(`/login?from=${location.pathname}`);
|
|
|
+ }
|
|
|
+ }, [location]);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 7.2 Router Provider实现
|
|
|
+
|
|
|
+```jsx
|
|
|
+import { useNavigate } from 'react-router-dom';
|
|
|
+import { useAuth } from '../hooks/useAuth';
|
|
|
+
|
|
|
+export default function Protected() {
|
|
|
+ const navigate = useNavigate();
|
|
|
+ // 页面访问鉴权
|
|
|
+ useAuth();
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="Protected">
|
|
|
+ <h3>受保护页面——需要登录后才可以访问</h3>
|
|
|
+ <div>
|
|
|
+ <button
|
|
|
+ onClick={() => {
|
|
|
+ if (window.confirm('确定退出系统吗')) {
|
|
|
+ localStorage.clear();
|
|
|
+ navigate('/login');
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ 退出登录
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
## 8. 其他
|
|
|
|
|
|
+### 8.1 路由懒加载
|
|
|
+
|
|
|
+> 为了提高React项目首次加载速度,推荐使用路由懒加载。
|
|
|
+
|
|
|
+实现时,需要从React核心库中引入`lazy`函数和`Suspense`组件。像这样,
|
|
|
+
|
|
|
+```jsx
|
|
|
+import { createBrowserRouter } from 'react-router-dom';
|
|
|
+import Home from '../pages/Home';
|
|
|
+import { lazy, Suspense } from 'react';
|
|
|
+
|
|
|
+const About = lazy(() => import('../pages/About'));
|
|
|
+
|
|
|
+const router = createBrowserRouter([
|
|
|
+ { path: '/', Component: Home },
|
|
|
+ {
|
|
|
+ path: '/about',
|
|
|
+ element: (
|
|
|
+ <Suspense fallback={<h3>加载中...</h3>}>
|
|
|
+ <About />
|
|
|
+ </Suspense>
|
|
|
+ ),
|
|
|
+ },
|
|
|
+]);
|
|
|
+
|
|
|
+export default router;
|
|
|
+```
|
|
|
+
|
|
|
+在定义路由时,React Router还提供了一系列组件:
|
|
|
+
|
|
|
+```jsx
|
|
|
+import './App.css';
|
|
|
+import { NavLink, Routes, Route } from 'react-router-dom';
|
|
|
+import { lazy, Suspense } from 'react';
|
|
|
+import Home from './pages/Home';
|
|
|
+
|
|
|
+// import About from './pages/About';
|
|
|
+// import User from './pages/User';
|
|
|
+import UserProfile from './pages/UserProfle';
|
|
|
+import UserPost from './pages/UserPost';
|
|
|
+
|
|
|
+const About = lazy(async () => {
|
|
|
+ await new Promise((resolve) => {
|
|
|
+ setTimeout(() => {
|
|
|
+ resolve();
|
|
|
+ }, 1000);
|
|
|
+ });
|
|
|
+ return import('./pages/About.jsx');
|
|
|
+});
|
|
|
+
|
|
|
+const User = lazy(() => import('./pages/User'));
|
|
|
+
|
|
|
+// 实现路由懒加载的高阶组件
|
|
|
+function withSuspense(Component) {
|
|
|
+ return (
|
|
|
+ <Suspense fallback={<h3>组件疯狂加载中...</h3>}>
|
|
|
+ <Component />
|
|
|
+ </Suspense>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+function App() {
|
|
|
+ return (
|
|
|
+ <div className="App">
|
|
|
+ <header className="App-header">
|
|
|
+ <NavLink className="App-link" to={'/'}>
|
|
|
+ 主页
|
|
|
+ </NavLink>{' '}
|
|
|
+ <NavLink className="App-link" to={'/about'}>
|
|
|
+ 关于
|
|
|
+ </NavLink>
|
|
|
+ <NavLink className="App-link" to={'/user'}>
|
|
|
+ 用户
|
|
|
+ </NavLink>
|
|
|
+ </header>
|
|
|
+ <main>
|
|
|
+ <Routes>
|
|
|
+ <Route path="/" element={<Home />} />
|
|
|
+ <Route path="/about" element={withSuspense(About)} />
|
|
|
+ <Route path="/user" element={withSuspense(User)}>
|
|
|
+ <Route index />
|
|
|
+ <Route path="profile" element={<UserProfile />} />
|
|
|
+ <Route
|
|
|
+ path="post"
|
|
|
+ // 并不会起作用。它只能在RouterProvider API中使用
|
|
|
+ lazy={() => import('./pages/UserPost.jsx')}
|
|
|
+ element={<UserPost />}
|
|
|
+ />
|
|
|
+ </Route>
|
|
|
+ <Route path="/list/:lid" />
|
|
|
+ </Routes>
|
|
|
+ </main>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export default App;
|
|
|
+```
|
|
|
+
|
|
|
+其中,`Route`组件就是定义路由的。当其path属性和当前地址匹配成功后,就会在其位置下渲染element绑定的React元素。
|
|
|
+
|
|
|
+同时可以定义多个`Route`组件,这样就可以在页面中定义多个路由。并且这些`Route`组件必须作为`Routes`组件的直接子代元素。
|
|
|
+
|
|
|
+为了将路由功能注入到项目中,需要在App组件中将整个项目代码包裹在一个`BrowserRouter`或者`HashRouter`组件中。
|
|
|
+
|
|
|
+- BrowserRouter 会实现一个基于History 模式的路由
|
|
|
+- HashRouter 会实现一个基于hash 模式的路由
|
|
|
+
|
|
|
|
|
|
|