软件开发定制定制前端实战|React18极客园——登陆模块(token持久化、路由拦截、mobx、封装axios)

软件开发定制定制欢迎来到我的博客
📔软件开发定制定制博主是一名大学在读本科生,软件开发定制定制主要学习方向是前端。
🍭软件开发定制定制目前已经更新了【Vue】、【React–软件开发定制定制从基础到实战】、【TypeScript】软件开发定制定制等等系列专栏
🛠软件开发定制定制目前正在学习的是🔥 R e a c t 框架 React框架 React框架🔥,软件开发定制定制中间穿插了一些基础知识的回顾
🌈博客主页👉

😇本文目录😇

软件开发定制定制本文被专栏收录
🕹坚持创作✏️,一起学习📖,码出未来👨🏻‍💻!

软件开发定制定制最近在学习React过程中,找到了一个实战小项目,在这里与大家分享。
本文遵循项目开发流程,逐步完善各个需求
前文——

登陆模块

1.基本结构模块

本节目标: 能够使用antd搭建基础布局

实现步骤

  1. 在 Login/index.js 中创建登录页面基本结构
  2. 在 Login 目录中创建 index.scss 文件,指定组件样式
  3. 将 logo.png 和 login.png 拷贝到 assets 目录中

代码实现

pages/Login/index.js

import { Card } from 'antd'import logo from '@/assets/logo.png'import './index.scss'const Login = () => {  return (    <div className="login">      <Card className="login-container">        <img className="login-logo" src={logo} alt="" />        {/* 登录表单 */}      </Card>    </div>  )}export default Login
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

pages/Login/index.scss

.login {  width: 100%;  height: 100%;  position: absolute;  left: 0;  top: 0;  background: center/cover url('~@/assets/login.png');    .login-logo {    width: 200px;    height: 60px;    display: block;    margin: 0 auto 20px;  }    .login-container {    width: 440px;    height: 360px;    position: absolute;    left: 50%;    top: 50%;    transform: translate(-50%, -50%);    box-shadow: 0 0 50px rgb(0 0 0 / 10%);  }    .login-checkbox-label {    color: #1890ff;  }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

2. 创建表单结构

本节目标: 能够使用antd的Form组件创建登录表单

实现步骤

  1. 打开 antd
  2. 找到代码演示的第一个示例(基本使用),点击<>(显示代码),并拷贝代码到组件中
  3. 分析 Form 组件基本结构
  4. 调整 Form 组件结构和样式

代码实现

pages/Login/index.js

import { Form, Input, Button, Checkbox } from 'antd'const Login = () => {  return (    <Form>      <Form.Item>        <Input size="large" placeholder="请输入手机号" />      </Form.Item>      <Form.Item>        <Input size="large" placeholder="请输入验证码" />      </Form.Item>      <Form.Item>        <Checkbox className="login-checkbox-label">          我已阅读并同意「用户协议」和「隐私条款」        </Checkbox>      </Form.Item>      <Form.Item>        <!-- 渲染Button组件为submit按钮 -->        <Button type="primary" htmlType="submit" size="large" block>          登录        </Button>      </Form.Item>    </Form>  )}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

3. 表单校验实现

本节目标: 能够为手机号和密码添加表单校验

实现步骤

  1. 为 Form 组件添加 validateTrigger 属性,指定校验触发时机的集合
  2. 为 Form.Item 组件添加 name 属性,这样表单校验才会生效
  3. 为 Form.Item 组件添加 rules 属性,用来添加表单校验

代码实现

page/Login/index.js

const Login = () => {  return (    <Form validateTrigger={['onBlur', 'onChange']}>      <Form.Item        name="mobile"        rules={[          {            pattern: /^1[3-9]\d{9}$/,            message: '手机号码格式不对',            validateTrigger: 'onBlur'          },          { required: true, message: '请输入手机号' }        ]}      >        <Input size="large" placeholder="请输入手机号" />      </Form.Item>      <Form.Item        name="code"        rules={[          { len: 6, message: '验证码6个字符', validateTrigger: 'onBlur' },          { required: true, message: '请输入验证码' }        ]}      >        <Input size="large" placeholder="请输入验证码" maxLength={6} />      </Form.Item>      <Form.Item name="remember" valuePropName="checked">        <Checkbox className="login-checkbox-label">          我已阅读并同意「用户协议」和「隐私条款」        </Checkbox>      </Form.Item>      <Form.Item>        <Button type="primary" htmlType="submit" size="large" block>          登录        </Button>      </Form.Item>    </Form>  )}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

4. 获取登录表单数据

本节目标: 能够拿到登录表单中用户的手机号码和验证码

实现步骤

  1. 为 Form 组件添加 onFinish 属性,该事件会在点击登录按钮时触发
  2. 创建 onFinish 函数,通过函数参数 values 拿到表单值
  3. Form 组件添加 initialValues 属性,来初始化表单值

代码实现

pages/Login/index.js

// 点击登录按钮时触发 参数values即是表单输入数据const onFinish = values => {  console.log(values)}<Form  onFinish={ onFinish }  initialValues={{    mobile: '13911111111',    code: '246810',    remember: true  }}>...</Form>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

5. 封装http工具模块

本节目标: 封装axios,简化操作

实现步骤

  1. 创建 utils/http.js 文件
  2. 创建 axios 实例,配置 baseURL,请求拦截器,响应拦截器
  3. 在 utils/index.js 中,统一导出 http

代码实现

utils/http.js

import axios from 'axios'const http = axios.create({  baseURL: 'http://geek.itheima.net/v1_0',  timeout: 5000})// 添加请求拦截器http.interceptors.request.use((config)=> {    return config  }, (error)=> {    return Promise.reject(error)})// 添加响应拦截器http.interceptors.response.use((response)=> {    // 2xx 范围内的状态码都会触发该函数。    // 对响应数据做点什么    return response  }, (error)=> {    // 超出 2xx 范围的状态码都会触发该函数。    // 对响应错误做点什么    return Promise.reject(error)})export { http }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

utils/index.js

import { http } from './http'export {  http }
  • 1
  • 2

6. 配置登录Mobx

本节目标: 基于mobx封装管理用户登录的store

store/login.Store.js

// 登录模块import { makeAutoObservable } from "mobx"import { http } from '@/utils'class LoginStore {  token = ''  constructor() {    makeAutoObservable(this)  }  // 登录  login = async ({ mobile, code }) => {    const res = await http.post('http://geek.itheima.net/v1_0/authorizations', {      mobile,      code    })    this.token = res.data.token  }}export default LoginStore
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

store/index.js

import React from "react"import LoginStore from './login.Store'class RootStore {  // 组合模块  constructor() {    this.loginStore = new LoginStore()  }}// 导入useStore方法供组件使用数据const StoresContext = React.createContext(new RootStore())export const useStore = () => React.useContext(StoresContext)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

7. 实现登录逻辑

本节目标: 在表单校验通过之后通过封装好的store调用登录接口

实现步骤

  1. 使用useStore方法得到loginStore实例对象
  2. 在校验通过之后,调用loginStore中的login函数
  3. 登录成功之后跳转到首页

代码实现

import { useStore } from '@/store'const onFinish = async (values) => {    // 存储登录成功的token    try {      await loginStore.setToken(values)      navigate('/', { replace: true })      message.success('At Your Service, Sir!', 2)    } catch (error) {      message.error(error.response?.data?.message || '登录失败')    }  };  const onFinishFailed = (errorInfo) => {    const [name] = errorInfo.errorFields[0].name    if (name === "captcha") message.error('登录失败,请检查验证码是否有误!', 2);    if (name === "tel") message.error('登录失败,请检查手机号是否有误!', 2);  }  return (...)}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

8. token持久化

封装工具函数

本节目标: 能够统一处理 token 的持久化相关操作,确保刷新后 token 不丢失。

实现步骤

  1. 创建 utils/token.js 文件
  2. 分别提供 getToken/setToken/clearToken/isAuth 四个工具函数并导出
  3. 创建 utils/index.js 文件,统一导出 token.js 中的所有内容,来简化工具函数的导入
  4. 将登录操作中用到 token 的地方,替换为该工具函数

代码实现

utils/token.js

const TOKEN_KEY = 'geek_pc'const getToken = () => localStorage.getItem(TOKEN_KEY)const setToken = token => localStorage.setItem(TOKEN_KEY, token)const clearToken = () => localStorage.removeItem(TOKEN_KEY)export { getToken, setToken, clearToken }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

持久化设置

本节目标: 使用token函数持久化配置

实现步骤

  1. 拿到token的时候一式两份,存本地一份
  2. 初始化的时候优先从本地取,取不到再初始化为控制

代码实现

store/login.Store.js

// 登录模块import { makeAutoObservable } from "mobx"import { setToken, getToken, clearToken, http } from '@/utils'class LoginStore {  // 这里哦!!  token = getToken() || ''  constructor() {    makeAutoObservable(this)  }  // 登录  login = async ({ mobile, code }) => {    const res = await http.post('http://geek.itheima.net/v1_0/authorizations', {      mobile,      code    })    this.token = res.data.token    // 还有这里哦!!    setToken(res.data.token)  } }export default LoginStore
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

9. 拦截器注入token

本节目标: 把token通过请求注入到请求头中

拼接方式:config.headers.Authorization = Bearer ${token}}

utils/http.js

http.interceptors.request.use(config => {  const token = getToken('pc-key')  if (token) {    config.headers.Authorization = `Bearer ${token}`  }  return config})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

第一次发起请求,是登录请求,此时,localStorage中没有token,getToken获取不到,不走下面这个if函数体,直接return config;

后面再发请求时,由于已经登录了,此时,localStorage中有token,getToken获取到了,走if中的函数体,在发起请求前自动进行预处理,追加一个token,以便于访问需要权限的页面

为请求头对象(headers)中添加token验证的自定义字段(Authorization),作用是为了让需要验证才能使用的API能够使用(请求头中携带了token值则可通过验证)

在最后必须return config

10. 路由导航守卫

本节目标: 能够实现未登录时访问拦截并跳转到登录页面(路由鉴权实现)

实现思路

自己封装 AuthRoute 路由鉴权高阶组件,实现未登录拦截,并跳转到登录页面

思路为:判断本地是否有token,如果有,就返回子组件,否则就重定向到登录Login

实现步骤

  1. 在 components 目录中,创建 AuthRoute/index.js 文件
  2. 判断是否登录
  3. 登录时,直接渲染相应页面组件
  4. 未登录时,重定向到登录页面
  5. 将需要鉴权的页面路由配置,替换为 AuthRoute 组件渲染

代码实现

components/AuthRoute/index.js

// 路由鉴权// 1. 判断token是否存在// 2. 如果存在 直接正常渲染// 3. 如果不存在 重定向到登录路由import { Navigate } from "react-router-dom";import { getToken } from "@/utils";// 高阶组件:把一个组件当成另外一个组件的参数传入 然后通过一定的判断 返回新的组件// 这里的AuthRoute就是一个高阶组件function AuthRoute({ children }) {  // 获取token  const tokenStr = getToken()  // 如果token存在 直接正常渲染  if (tokenStr) {    return <>{children}</>  }  // 如果token不存在,重定向到登录路由  else {    return <Navigate to='/login' replace />  }}{/* <AuthRoute> <Layout /> </AuthRoute>  登录:<> <Layout /> </> 非登录:<Navigate to="/login" replace />*/ }export { AuthRoute }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

注:utils工具函数getToken如下

// 从localstorage中取tokenconst getToken = () => {return window.localStorage.getItem(key)}
  • 1
  • 2
  • 3
  • 4

src/routes/index.js路由表文件

import Layout from "@/pages/Layout";import Login from "@/pages/Login";import { AuthRoute } from "@/components/AuthRoute";// eslint-disable-next-lineexport default [  // 不需要鉴权的组件Login  {    path: "/login",    element: <Login />  },  // 需要鉴权的组件Layout  {    path: "/",    element: <AuthRoute>      <Layout />    </AuthRoute>  }]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

下篇文章:Layout布局模块的实现
专栏订阅入口

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发