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

文章目录

    • 一、项目起航:项目初始化与配置
    • 二、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.txtnpm 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.hqwc.cn/news/15985.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

排序之玩转qsort函数——【C语言】

说起排序&#xff0c;我们会想起许多算法&#xff0c;在之前的博客中我也写到过&#xff0c;比如&#xff1a;冒泡排序法、快速排序法、选择排序法等等。其实在C语言中一直有一个可以将数组中的内容进行排序的函数且功能完善内容齐全的库函数——qsort函数。今天就让我们来探索…

SpringBoot原理分析 | Spring Data整合:JDBC、Druid、Mybatis

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Spring Data Spring Data是一个用于简化数据库访问和操作的开源框架&#xff0c;为开发人员提供了一种通用的方式来处理不同类型的数据存储&#xff0c;例如关系型数据…

Spring Boot中的度量指标及使用方法

Spring Boot中的度量指标及使用方法 简介 Spring Boot是目前流行的Java后端框架之一&#xff0c;它提供了许多有用的功能&#xff0c;其中包括度量指标。度量指标可以帮助我们监测应用程序的性能、稳定性和可靠性&#xff0c;以便及时发现并解决问题。本文将介绍Spring Boot中…

OpenCV——总结《车牌识别》

1.图片中的hsv hsv提取蓝色部分 # hsv提取蓝色部分 def hsv_color_find(img):img_copy img.copy()cv2.imshow(img_copy, img_copy)"""提取图中的蓝色部分 hsv范围可以自行优化cv2.inRange()参数介绍&#xff1a;第一个参数&#xff1a;hsv指的是原图第二个参…

Java后端编译与优化

如果我们将字节码看作是程序语言的一种中间表示形式&#xff0c;那编译器无论在何时、在何种状态下把Class文件转换成与本地基础设施相关的二进制机器码&#xff0c;它都可以视为整个编译过程的后端。 1 即时编译器 即时编译器是指运行时将热点代码编译成本地机器码&#xff…

nginx反向代理 404 问题

发现我们设置了反向代理没有起作用&#xff0c;最后发现原来是伪静态惹得祸 解决nginx添加反向代理代码不生效-与原rewrite伪静态规则冲突了 以thinkphp官方给的伪静态为例 if (!-e $request_filename){rewrite ^(.*)$ /index.php?s$1 last; break;}仔细研究发现发现问…

第48步 深度学习图像识别:RegNet建模(Pytorch)

基于WIN10的64位系统演示 一、写在前面 &#xff08;1&#xff09;RegNet RegNet (Regulated Networks) 是一种由 Facebook AI 的研究者们在 2020 年提出的神经网络架构&#xff0c;旨在探索网络架构设计的各种可能性&#xff0c;并找出最优的网络设计规则。RegNet 的核心理…

全志V3S嵌入式驱动开发(四种启动方式)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 对于v3s的启动方式分析,怎么研究都不为过。对我们整个系列文章比较熟悉的同学来说,这幅图真的是太熟悉了, 整个流程不复杂。它主要是告诉我们,v3s加载的顺序是怎么样的…

基于智能手机的医院服务客户端设计与实现(论文+源码)_kaic

摘 要 近年来&#xff0c;随着中国经济的迅猛发展&#xff0c;医疗技术水平也在不断提高&#xff0c;但由于人口数目巨大&#xff0c;导致医疗资源人均分配不足的情况依旧十分严峻。预约挂号一直是制约医疗机构服务质量提高的主要环节之一。在传统预约挂号方式下&#xff0c;繁…

APP测试面试题快问快答(六)

26. App测试工程师到底测些什么&#xff1f; 考察点&#xff1a;是否有移动app测试的相关经验 界面测试 功能测试 兼容性测试 性能测试 稳定性测试 安全性测试 环境的测试 专项测试 –安装&#xff0c;卸载&#xff0c;流量&#xff0c;电量&#xff0c;弱网和FPS 27…

华为云函数工作流FunctionGraph新手操作指南

函数工作流&#xff08;FunctionGraph&#xff09;是华为云提供的一款无服务器&#xff08;Serverless&#xff09;计算服务&#xff0c;无服务器计算是一种托管服务&#xff0c;服务提供商会实时为你分配充足的资源&#xff0c;而不需要预留专用的服务器或容量&#xff0c;真正…

【LeetCode】217. 存在重复元素

217. 存在重复元素&#xff08;简单&#xff09; 方法一&#xff1a;哈希表长度比较 思路 针对重复元素&#xff0c;很容易就想到 set。我们可以先将 nums 中的所有元素存入set&#xff0c;然后比较两个数据结构的长度&#xff0c;如果相等则说明不存在重复元素&#xff0c;反…