IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> react-router-dom实现私密路由 -> 正文阅读

[JavaScript知识库]react-router-dom实现私密路由

场景

react项目里面有些时候部分页面需要登陆或者部分权限才能访问 这个时候需要私有路由

在创建私有路由之前,需要一种方法来确定用户是否被认证。这里说的是React Router保护路由的方法,而不是关于认证,所以使用一个假的useAuth Hook来确定我们用户的认证 “状态”。

创建文件APP.jsx, hooks/useAuth.jsx, Login.jsx, Nav.jsx

/pricing和/login路径是公开的,/dashboard和/settings路线将是私有的。现在,先是像普通路由一样渲染它们。

创建Login.jsx和Nav.jsx,vscode里快捷键rafce快速生成登录和退出

创建App.jsx,渲染路由

/**
 * @file App.jsx
 * @description 创建几个组件用于路由使用
 * 主页、定价、仪表板、设置和登录
 */
import { Routes, Route } from 'react-router-dom';
import Nav from './Nav';
import Login from './Login';

const Home = () => <h1>Home (Public)</h1>;
const Pricing = () => <h1>Pricing (Public)</h1>;

const Dashboard = () => <h1>Dashboard (Private)</h1>;
const Settings = () => <h1>Settings (Private)</h1>;


/**
 * @description /, /pricing, /login routes是共有
 * /dashboard, /settings route是私有
 */
export default function App() {
  return (
    <div>
      <Nav />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/pricing" element={<Pricing />} />
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
        <Route path="/login" element={<Login />} />
      </Routes>
    </div>
  );
}

在创建hooks/useAuth模拟确定我们用户的认证 “状态”。

有了这个useAuth就知道用户是否被授权、登录或注销
useAuth Hook可以有很多不同的工作方式。
也许它向API端点发出HTTP请求,以验证一个cookie。或者它可以解码存储在浏览器本地存储器中的JWT令牌。或者你可以使用第三方认证解决方案
总之:在任何情况下,目标都是一样的:找出用户当前是否已被认证。

import React from "react";
import { useContext, useState } from 'react'
const authContext = React.createContext()

function useAuth() {
  const [authed,setAuthed] = useState() //状态

   return {
    //认证状态
     authed,
     //登录
     login() {
       return new Promise ((res) => {
         setAuthed(true)
         res()
       })
     },
     //退出
     logout() {
       return new Promise((res)=>{
         setAuthed(false)
         res()
       })
     }
   }
}
//用context存储auth状态来进行传递
export function AuthProvider( {children} ) {
  const auth = useAuth()
  return <authContext.Provider value={auth}>{children}</authContext.Provider>
}

export default function AuthConsumer() {
  const { auth } = useContext(authContext);
  return auth
}

接下来进入Login.jsx完成登录授权

/**
 * @todo 现在开始做一些授权工作 首先完善登录组件。这个组件的目标自然是允许用户登录
 */
import React from 'react'
import { useNavigate } from "react-router-dom";
import useAuth  from './hooks/useAuth';

/**
 *当用户点击按钮时,调用login(从useAuth Hook获得),然后一旦他们登录,使用navigate,跳转到/dashboard。
 */
const Login = () => {
  const navigate = useNavigate();
  const { login } = useAuth();
  /**
  *@function
  *@description 登陆的同时authed状态为true并跳转/dashboard
  */
  const handleLogin = () => {
    login().then(() => {
      navigate("/dashboard");
    });
  };
  return (
    <div>
      <h1>Login</h1>
      <button onClick={handleLogin}>Log in</button>
    </div>
  )
}
export default Login

接下来Nav.jsx里面添加注销的功能

//接下来,让我们添加注销的功能。同样,我们已经有了来自useAuth Hook的注销方法,所以这也应该是简单地添加一些用户界面。所有的改变都将发生在我们的导航组件上。
import { useNavigate } from "react-router-dom";
import useAuth from "./useAuth";

const Nav = () => {
  const { authed, logout } = useAuth();
  const navigate = useNavigate();
  //点击退出authed为false
  const handleLogout = () => {
    logout();
    navigate("/");
  };
//authed 为true则有注销登录按钮
  return (
    <nav>
      <ul>
        <li>
          <Link to="/">Home</Link>
        </li>
        <li>
          <Link to="/pricing">Pricing</Link>
        </li>
      </ul>
      {authed && <button onClick={handleLogout}>Logout</button>}
    </nav>
  );
}
export default Nav

进入App.jsx中让/dashboard和/settings成为私有

在深入实施之前,先提出最终的API可能是什么样子的。对于每条我们希望是私有的路由,不给它的Routes element我们希望它直接呈现的组件,而是把它包在一个新的组件中,言简意赅的设置成RequireAuth。

最终Api的样子
/**
 * @description /, /pricing, /login routes是共有
 * /dashboard, /settings route是私有
 */
...
      <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/pricing" element={<Pricing />} />
          <Route
            path="/dashboard"
            element={
              <RequireAuth>
                <Dashboard />
              </RequireAuth>
            }
          />
          <Route
            path="/settings"
            element={
              <RequireAuth>
                <Settings />
              </RequireAuth>
            }
          />
          <Route path="/login" element={<Login />} />
      </Routes>
...

可以想到RequireAuth目的是让/dashboard和/setting成为私密路由,所以它的逻辑主要是实现两件事情。

首先,它的唯一api是一个children元素。

第二,如果用户通过了认证,它应该呈现那个children元素,如果没有,它应该把用户重定向到他们可以认证的页面

使用我们先前的useAuth Hook来建立<RequireAuth>

/**
 * 用它包裹想要验证的路由,此时当一个没有经过认证的用户试图进入/dashboard或/settings时
 * 他们会被重定向到/login。然后一旦他们登录,我们就把他们重定向到/dashboard。
 *reactrouterdom6中一个<Navigate>元素在渲染时改变当前位置,它是一个围绕useNavigate的组件包
 *装,并接受所有与props相同的参数。他替代了5版本中Switch中的<Redirect>
 */
const RequireAuth = ({ children }) => {
  const { authed } = useAuth();
  return authed === true ? children : <Navigate to="/login" replace />;
}

基本的功能就实现了

补充优化<RequireAuth/>

上面的代码总是将用户重定向到/dashboard,开发中不应该这样子,而应该将他们重定向到他们最初试图访问的路线。例如,如果他们试图访问/settings但没有登录,在我们重定向他们并且他们登录后,我们应该把他们带回/settings,而不是dashboard。
于是要用到useLocation这个钩子返回当前的位置对象,同时
reactrouterdom6中<Navigate>组件在渲染时改变当前位置,有两个参数replace和state

const RequireAuth = ({ children })=> {
  const { authed } = useAuth();
  const location = useLocation();

  return authed === true ? (
    children
  ) : (
  //使用location.state来保留之前的位置,这样你就可以在用户认证后把他们送到那里。
  //replace: true 来替换历史栈中的/login路由,这样用户在登录后点击返回按钮时就不会返回到登录页面。
    <Navigate to="/login" replace state={{ path: location.pathname }} />
  )
 }

最后,完善Login组件

目的:在用户认证后,如果原路径存在,我们会将用户重定向到原路径,如果不存在,我们会将他们带到/dashboard。 可以使用React Router的useLocation Hook来获得对location.state的访问,其中会有我们的path属性。

import { useNavigate,useLocation  } from "react-router-dom";
import useAuth  from './hooks/useAuth';

const Login = () => {
  const navigate = useNavigate();
  const { login } = useAuth();
  const { state } = useLocation();
  /**
  *@function
  *@description 登陆跳转
  */
  const handleLogin = () => {
    login().then(() => {
    //如果原路径存在,会将用户重定向到原路径,如果不存在,我们会将他们带到/dashboard
      navigate(state?.path || "/dashboard"); 
    });
  };
  return (
    <div>
      <h1>Login</h1>
      <button onClick={handleLogin}>Log in</button>
    </div>
  )
}

export default Login

完毕

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-09 18:14:27  更:2022-04-09 18:16:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:32:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码