【实战】 JWT、用户认证与异步请求(上) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(四)

news2024/11/19 10:21:34

文章目录

    • 一、项目起航:项目初始化与配置
    • 二、React 与 Hook 应用:实现项目列表
    • 三、TS 应用:JS神助攻 - 强类型
    • 四、JWT、用户认证与异步请求
      • 1.login
      • 2.middleware of json-server
      • 3.jira-dev-tool(imooc-jira-tool)
        • 安装
        • 问题
          • 问题一
          • 问题二
        • 使用
      • 4.JWT原理与auth-provider实现
        • 注册一个新用户
        • auth-provider
      • 5.useContext(user,login,register,logout)


学习内容来源:React + React Hook + TS 最佳实践-慕课网


相对原教程,我在学习开始时(2023.03)采用的是当前最新版本:

版本
react & react-dom^18.2.0
react-router & react-router-dom^6.11.2
antd^4.24.8
@commitlint/cli & @commitlint/config-conventional^17.4.4
eslint-config-prettier^8.6.0
husky^8.0.3
lint-staged^13.1.2
prettier2.8.4
json-server0.17.2
craco-less^2.0.0
@craco/craco^7.1.0
qs^6.11.0
dayjs^1.11.7
react-helmet^6.1.0
@types/react-helmet^6.1.6
react-query^6.1.0
@welldone-software/why-did-you-render^7.0.1
@emotion/react & @emotion/styled^11.10.6

具体配置、操作和内容会有差异,“坑”也会有所不同。。。


一、项目起航:项目初始化与配置

  • 【实战】 项目起航:项目初始化与配置 —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(一)

二、React 与 Hook 应用:实现项目列表

  • 【实战】 React 与 Hook 应用:实现项目列表 —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二)

三、TS 应用:JS神助攻 - 强类型

  • 【实战】 TS 应用:JS神助攻 - 强类型 —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(三)

四、JWT、用户认证与异步请求

1.login

  • 新建文件:src\screens\login\index.tsx
import { FormEvent } from "react";

const apiUrl = process.env.REACT_APP_API_URL;

export const Login = () => {
  const login = (param: { username: string; password: string }) => {
    fetch(`${apiUrl}/login`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(param),
    }).then(async (res) => {
      if (res.ok) {
      }
    });
  };

  // HTMLFormElement extends Element (子类型继承性兼容所有父类型)(鸭子类型:duck typing: 面向接口编程 而非 面向对象编程)
  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const username = (event.currentTarget.elements[0] as HTMLFormElement).value;
    const password = (event.currentTarget.elements[1] as HTMLFormElement).value;
    login({ username, password });
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="username">用户名</label>
        <input type="text" id="username" />
      </div>
      <div>
        <label htmlFor="password">密码</label>
        <input type="password" id="password" />
      </div>
      <button type="submit">登录</button>
    </form>
  );
};
  • src\App.tsx 中引入:
import "./App.css";
import { Login } from "screens/login";

function App() {
  return (
    <div className="App">
      <Login />
    </div>
  );
}

export default App;

目前页面点击登录 404,下一步配置 json-server 中间件,使其可以模拟 非 restful 接口

2.middleware of json-server

  • 新建文件:__json_server_mock__\middleware.js
module.exports = (req, res, next) => {
  if (req.method === "POST" && req.path === "/login") {
    if (req.body.username === "user" && req.body.password === "123") {
      return res.status(200).json({
        user: {
          token: "token123",
        },
      });
    } else {
      return res.status(400).json({ message: "用户名或者密码错误" });
    }
  }
  next();
};
  • 配置 package.jsonjson-serverscript
"json-server": "json-server __json_server_mock__/db.json -w -p 3001 --middlewares ./__json_server_mock__/middleware.js"
  • 配置完后重新启动 json-server ,输入中间件预置用户名和密码即可正常访问(200),否则(400:bad request)

3.jira-dev-tool(imooc-jira-tool)

jira-dev-tool - npm

安装

  • 首先确认 git 工作区 clean,安装 jira-dev-tool(imooc-jira-tool)
npx imooc-jira-tool
  • 引入到 src\index.tsx
import { loadDevTools } from "jira-dev-tool";

loadDevTools(() => {
  ReactDOM.render(
    <React.StrictMode>
      <AppProviders>
        <App />
      </AppProviders>
    </React.StrictMode>,
    document.getElementById("root")
  );
});

这一步后页面会多出一个“齿轮”,点击即可使用 jira-dev-tool

问题

安装 jira-dev-tool(imooc-jira-tool)后启动项目联调可能会出现的问题

问题一
  • 报错:
request (TypeError: Failed to fetch). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.
  • 解决:
npx msw init ./public/ --save
问题二

由于 jira-dev-tool 已经两年没有更新了,且依赖 react@“^16.0.0”, 若要继续使用,在 npm i 时会有如下报错:

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: jira@0.1.0
npm ERR! Found: react@18.2.0
npm ERR! node_modules/react
npm ERR!   react@"^18.2.0" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^16.0.0" from jira-dev-tool@1.7.61       
npm ERR! node_modules/jira-dev-tool
npm ERR!   jira-dev-tool@"^1.7.61" from the root project      
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry       
npm ERR! this command with --force or --legacy-peer-deps      
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR!
npm ERR! For a full report see:
npm ERR! C:\...\npm-cache\_logs\2023-03-08T09_11_24_998Z-eresolve-report.txt

npm ERR! A complete log of this run can be found in:
npm ERR! C:\...\npm-cache\_logs\2023-03-08T09_11_24_998Z-debug-0.log

解决方案一:

  • 删掉文件 yarn.lock,以及package.json 中的 "jira-dev-tool": "^1.7.61", 部分,jira-dev-tool 手动安装

解决方案二(推荐)

  • 使用 yarn 代替 npm i

使用

  • 开发者工具用 MSW 以 Service Worker 为原理实现了"分布式后端"
  • 后端逻辑处理后,以localStorage为数据库进行增删改查操作
  • 浏览器上安装了一个独立的后端服务和数据库,再也不受任何中心化服务的影响 点击’清空数据库’便可以重置后端服务
  • 可以精准地控制 HTTP请求的时间、失败概率、失败规则
  • Service Worker + localStorage虽然本质上与传统后端服务并不同,但丝毫不会影响前端开发

其他具体操作可见文档以及接下来的操作:jira-dev-tool - npm

  • Service Worker API - Web API 接口参考 | MDN

安装好后进入/login,请求login接口,可以看到状态码后带有(from service worker)字样即成功连接:

在这里插入图片描述

开发工具控制台第一个tab页设置请求最短时间、请求失败比例:

在这里插入图片描述

开发工具控制台将/login添加到异步请求失败设置中,状态码 400 变为 500,提示:“请求失败,请检查 jira-dev-tool 的设置”:
在这里插入图片描述

4.JWT原理与auth-provider实现

注册一个新用户

  • 修改:src\screens\login\index.tsx
    • 调用接口 login 改为 register
    • 按钮 登录 改为 注册

注册一个新用户 jira(密码:jira),接口返回:

{
  "user": {
    "id": 2087569429,
    "name": "jira",
    "token": "MjA4NzU2OTQyOQ=="
  }
}

token 即是 JWT(JSON Web Tokens) 的产物

  • JSON Web Tokens - jwt.io

auth-provider

修改 src\screens\ProjectList\components\SearchPanel.tsx,为 User 新增 token:

export interface User {
  id: string;
  name: string;
  email: string;
  title: string;
  organization: string;
  token: string;
}
...

新建 src\auth-provider.ts

模拟第三方服务

// 在真实环境中,如果使用了 firebase 这种第三方 auth 服务的话,本文件不需要开发者开发

import { User } from "screens/ProjectList/components/SearchPanel"

const localStorageKey = '__auth_provider_token__'
const apiUrl = process.env.REACT_APP_API_URL;

export const getToken = () => window.localStorage.getItem(localStorageKey)

export const handleUserResponse = ({user} : { user: User }) => {
  window.localStorage.setItem(localStorageKey, user.token || '')
  return user
}

export const login = (data: { username: string, password: string }) => {
  return fetch(`${apiUrl}/login`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(data),
  }).then(async (res) => {
    if (res.ok) {
      return handleUserResponse(await res.json())
    } else {
      return Promise.reject(data)
    }
  });
}

export const register = (data: { username: string, password: string }) => {
  return fetch(`${apiUrl}/register`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(data),
  }).then(async (res) => {
    if (res.ok) {
      return handleUserResponse(await res.json())
    } else {
      return Promise.reject(data)
    }
  });
}

export const logout = async () => window.localStorage.removeItem(localStorageKey)

细节点:

  • 函数定义时,值前添加 async 使其返回一个 Promise 对象
  • 回调函数入参和回调函数内有且只有一个函数调用且它的入参与回调函数入参一致,该回调函数可直接简写为其内部的函数调用且不带参(这是函数式编程-PointFree的一种应用):
    • const login = (form: AuthForm) => auth.login(form).then(user => setUser(user))
    • const login = (form: AuthForm) => auth.login(form).then(setUser)

【笔记】函数式编程——PointFree

5.useContext(user,login,register,logout)

新建 src\context\auth-context.tsx

import React, { ReactNode, useState } from "react"
import * as auth from 'auth-provider'
import { User } from "screens/ProjectList/components/SearchPanel"

interface AuthForm {
  username: string,
  password: string
}

const AuthContext = React.createContext<{
  user: User | null,
  login: (form : AuthForm) => Promise<void>,
  register: (form : AuthForm) => Promise<void>,
  logout: () => Promise<void>,
} | undefined>(undefined)

AuthContext.displayName = 'AuthContext'

export const AuthProvider = ({children}:{children: ReactNode}) => {
  // 这里要考虑到初始值的类型与后续值类型,取并组成一个泛型
  const [user, setUser] = useState<User | null>(null)

  const login = (form: AuthForm) => auth.login(form).then(user => setUser(user))
  const register = (form: AuthForm) => auth.register(form).then(user => setUser(user))
  const logout = () => auth.logout().then(() => setUser(null))

  return <AuthContext.Provider children={children} value={{ user, login, register, logout }}/>
}

export const useAuth = () => {
  const context = React.useContext(AuthContext)
  if (!context) {
    throw new Error('useAuth 必须在 AuthProvider 中使用')
  }
  return context
}

新建 src\context\index.tsx

import { ReactNode } from "react";
import { AuthProvider } from "./auth-context";

export const AppProvider = ({children}:{children: ReactNode}) => {
  return <AuthProvider>
    {children}
  </AuthProvider>
}

在项目中使用 AppProvider,修改 src\index.tsx

import { AppProvider } from "context";
...
loadDevTools(() => {
  root.render(
    // <React.StrictMode>
    <AppProvider>
      <App />
    </AppProvider>
    // </React.StrictMode>
  );
});
...

修改 src\screens\login\index.tsx,调用 useAuth 中的 login,并使用之前注册的账号 jira(jira) 验证:

import { useAuth } from "context/auth-context";
import { FormEvent } from "react";

export const Login = () => {
  const {login, user} = useAuth()
  // HTMLFormElement extends Element (子类型继承性兼容所有父类型)(鸭子类型:duck typing: 面向接口编程 而非 面向对象编程)
  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {...};
  return (
    <form onSubmit={handleSubmit}>
      <div>
        {
          user ? <div>
            登录成功,用户名{user?.name}
          </div> : null
        }
      </div>
      <div>
        <label htmlFor="username">用户名</label>
        <input type="text" id="username" />
      </div>
      <div>
        <label htmlFor="password">密码</label>
        <input type="password" id="password" />
      </div>
      <button type="submit">登录</button>
    </form>
  );
};


部分引用笔记还在草稿阶段,敬请期待。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/691731.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习——自然语言处理(一)

1 分词 1.1 设计原则 切分粒度大&#xff1b;非字典词少、单字字典词少&#xff1b;总体次数少。 1.2 基于词典匹配的分词 1.3 基于语法和规则的分词 目前处在试验阶段 1.4 基于统计的分词 1.5 技术难点 1.5.1 歧义识别 交集型歧义&#xff1a;AB | C or A | BC 组合型…

Jmeter操作数据库运行提示“Cannot load JDBC driver class ‘com.mysql.jdbc.Driver‘”的有效解决

如图所示&#xff0c;在jmeter中运行sql时报错提示“Cannot load JDBC driver class com.mysql.jdbc.Driver” 原因分析&#xff1a;这是因为没有mysql驱动&#xff0c;需要下载对应的jar包 一、下载地址&#xff1a;MySQL :: Download Connector/J 根据需求选择下载&#xf…

数字化转型:智慧物业行业落地与应用的突围之路!

导语 | 红杉中国在《2021 年企业数字化年度指南》中指出&#xff0c;96% 的受访企业已经开展了数字化实践&#xff0c;而其中超过 6 成的受访者都表示期望在未来进一步增加数字化的投入。技术因素或将成为未来两到三年影响企业发展最为重要的外部力量。当前地产与物业行业进入不…

当前最强的免费AI画图、AI绘图工具-2

Midjourney比较贵&#xff0c;而且无法访问&#xff0c;Stable Diffusion部署起来很麻烦。网上有哪些可以直接在网页端或者下载的app可以实现AI画图的工具。我们整理了45个相关工具&#xff0c;这是系列2&#xff0c;收录到 当前最强的免费AI画图、AI绘图工具-2https://www.web…

【C++】-- 高并发内存池

高并发内存池 项目介绍池化技术内存池 定长内存池的实现整体框架threadcachethreadcache整体设计threadcache哈希桶映射对齐规则TLS无锁访问 centralcachecentralcache整体设计centralcache结构设计centralcache的实现 pagecachepagecache整体设计pagecache中获取Span 回收内存…

【C/C++练习】经典的快慢指针问题---移除元素

&#x1f4d6;题目描述 题目出处&#xff1a;移除元素 &#x1f516;示例 &#x1f4d6;题解 对于本题我将按照由易到难的顺序为大家分享三种解题思路&#xff0c;并逐一分析它们的优劣&#xff0c;以及注意事项。 &#x1f516;思路一&#xff1a;暴力求解 我想暴力求解应该…

零-云尚办公项目学习

对于云尚办公项目的学习 1、这是尚硅谷推出的新的OA项目 云尚办公系统是一套自动办公系统&#xff0c;系统主要包含&#xff1a;管理端和员工端 管理端包含:权限管理、审批管理、公众号菜单管理 员工端:采用微信公众号操作&#xff0c;包含&#xff1a;办公审批、微信授权登…

数字通信中的编码(学习笔记)

编码种类 RZ(Return Zero Code)编码 也称为归零码&#xff0c;就是在 一个周期内&#xff0c;用二进制传输数据位&#xff0c;在数据脉冲结束后&#xff0c;需要维持一段时间的低电平。 RZ编码又分为两种&#xff1a; 单极性归零码 低电平表示0&#xff0c;正电平表示1&…

【Java用法】windows10系统下修改jar中的文件并重新打包成jar文件然后运行

windows10系统下修改jar中的文件并重新打包成jar文件然后运行 一、背景描述二、操作步骤2.1 解压jar包2.2 修改配置文件2.3 重新打成jar包2.4 确认是否修改成功2.5 运行程序 一、背景描述 测试环境&#xff08;Linux&#xff09;的代码&#xff08;jar包&#xff09;拉取到本地…

AI数字人:语音驱动面部模型及超分辨率重建Wav2Lip-HD

1 Wav2Lip-HD项目介绍 数字人打造中语音驱动人脸和超分辨率重建两种必备的模型&#xff0c;它们被用于实现数字人的语音和图像方面的功能。通过Wav2Lip-HD项目可以快速使用这两种模型&#xff0c;完成高清数字人形象的打造。 项目代码地址&#xff1a;github地址 1.1…

可再生能源与能源存储技术的结合和互补

在全球对可再生能源的需求日益增长的背景下&#xff0c;如何将可再生能源与能源存储技术相结合&#xff0c;实现能源的高效利用和持续供应成为了一个重要的议题。本文将探讨可再生能源与能源存储技术的结合与互补关系&#xff0c;分析其对能源领域的影响以及未来发展的前景。 …

CSS常用样式

文章目录 字体样式文本样式颜色和背景样式对齐方式下划线、上划线、删除线设置行高 列表样式背景样式背景颜色背景图片背景重复背景大小 鼠标样式伪类样式设置透明度 字体样式 所有样式都写在<style>标签内&#xff0c;里面加选择器 <!DOCTYPE html> <html>…

别小看可拖拽式表单设计器,降本增效就靠它啦!

在经济快速发展的当下&#xff0c;办公已然进入流程化发展阶段。不少企业希望实现降本增效的办公效果&#xff0c;大家不妨可以了解下可拖拽式表单设计器。通过简单的拖拉拽就能实现应用组建&#xff0c;创建属于自己的快速开发框架平台&#xff0c;不仅省下培养专业程序人工的…

安科瑞电化学储能电能管理系统解决方案

1.概述 在我国新型电力系统中&#xff0c;新能源装机容量逐年提高&#xff0c;但是新能源比如光伏发电、风力发电是不稳定的能源&#xff0c;所以要维持电网稳定&#xff0c;促进新能源发电的消纳&#xff0c;储能将成为至关重要的一环&#xff0c;是分布式光伏、风电等新能源…

抖音本地生活团购软件开发

抖音本地生活团购软件开发需要考虑以下几个方面&#xff1a; 功能设计&#xff1a;根据本地生活团购服务特点&#xff0c;设计相应的功能模块&#xff0c;如商家入驻、商品展示、订单管理、支付等。 技术选型&#xff1a;选择适合该项目的技术和框架&#xff0c;如移动…

【MySQL经典练习题】1. 多列数据求最大值

用 SQL 从多行数据里选出最大值或最小值很容易——通过 GROUP BY 子句对合适的列进行聚合操作&#xff0c;并使用 MAX 或 MIN 聚合函数就可以求出。 那么&#xff0c;从多列数据里选出最大值该怎么做呢&#xff1f; 目录 1、建表SQL 2、查询SQL &#xff08;1&…

LabVIEW开发汽车装配挡风玻璃清洗机灌装机

LabVIEW开发汽车装配挡风玻璃清洗机灌装机 挡风玻璃清洗机灌装机用于填充车内的肥皂槽。该项目在汽车行业实施。可编程逻辑控制器用于许多类型的行业&#xff0c;它使系统灵活。以前使用继电器逻辑&#xff0c;但由于其局限性&#xff0c;用PLC代替了。PLC用于模拟和数字逻辑信…

Thymeleaf介绍及其在Spring Boot中的使用

&#x1f4d6; Thymeleaf简介 &#x1f4da; Thymeleaf的定义 Thymeleaf 是一款现代化的服务器端 Java 模板引擎&#xff0c;适用于 Web 和独立应用场景。它具备处理 HTML、XML、JavaScript、CSS 以及纯文本的能力。Thymeleaf 的核心目标是为开发者提供一种优雅且自然的模板设…

vue 图片上传到腾讯云对象存储组件封装(完善版)

vue 上传图片到腾讯云对象存储 1、 引入cos-js-sdk-v52、封装uploadcos.js3、封装图片上传组件、调用上传方法4、页面使用组件 之前总结过 vue 封装图片上传组件到腾讯云对象存储&#xff0c;后来又加了一些功能&#xff0c;在图片过大时进行压缩&#xff0c;压缩完成之后&…

基于NXP i.MX 6ULL——MQTT通信协议的开发案例

前 言 本指导文档适用开发环境&#xff1a; Windows开发环境&#xff1a;Windows 7 64bit、Windows 10 64bit Linux开发环境&#xff1a;Ubuntu 18.04.4 64bit 拟机&#xff1a;VMware15.1.0 U-Boot&#xff1a;U-Boot-2020.04 Kernel&#xff1a;Linux-5.4.70 Linux S…