国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【實(shí)戰(zhàn)】 JWT、用戶認(rèn)證與異步請求(1) —— React17+React Hook+TS4 最佳實(shí)踐,仿 Jira 企業(yè)級項(xiàng)目(四)

這篇具有很好參考價(jià)值的文章主要介紹了【實(shí)戰(zhàn)】 JWT、用戶認(rèn)證與異步請求(1) —— React17+React Hook+TS4 最佳實(shí)踐,仿 Jira 企業(yè)級項(xiàng)目(四)。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。


學(xué)習(xí)內(nèi)容來源:React + React Hook + TS 最佳實(shí)踐-慕課網(wǎng)


相對原教程,我在學(xué)習(xí)開始時(shí)(2023.03)采用的是當(dāng)前最新版本:

項(xiàng) 版本
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
prettier 2.8.4
json-server 0.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

具體配置、操作和內(nèi)容會(huì)有差異,“坑”也會(huì)有所不同。。。


一、項(xiàng)目起航:項(xiàng)目初始化與配置

  • 【實(shí)戰(zhàn)】 一、項(xiàng)目起航:項(xiàng)目初始化與配置 —— React17+React Hook+TS4 最佳實(shí)踐,仿 Jira 企業(yè)級項(xiàng)目(一)

二、React 與 Hook 應(yīng)用:實(shí)現(xiàn)項(xiàng)目列表

  • 【實(shí)戰(zhàn)】 二、React 與 Hook 應(yīng)用:實(shí)現(xiàn)項(xiàng)目列表 —— React17+React Hook+TS4 最佳實(shí)踐,仿 Jira 企業(yè)級項(xiàng)目(二)

三、TS 應(yīng)用:JS神助攻 - 強(qiáng)類型

  • 【實(shí)戰(zhàn)】 三、TS 應(yīng)用:JS神助攻 - 強(qiáng)類型 —— React17+React Hook+TS4 最佳實(shí)踐,仿 Jira 企業(yè)級項(xiàng)目(三)

四、JWT、用戶認(rèn)證與異步請求

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: 面向接口編程 而非 面向?qū)ο缶幊?
  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;

目前頁面點(diǎn)擊登錄 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: "用戶名或者密碼錯(cuò)誤" });
    }
  }
  next();
};
  • 配置 package.jsonjson-serverscript
"json-server": "json-server __json_server_mock__/db.json -w -p 3001 --middlewares ./__json_server_mock__/middleware.js"
  • 配置完后重新啟動(dòng) json-server ,輸入中間件預(yù)置用戶名和密碼即可正常訪問(200),否則(400:bad request)

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

jira-dev-tool - npm

安裝
  • 首先確認(rèn) git 工作區(qū) 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")
  );
});

這一步后頁面會(huì)多出一個(gè)“齒輪”,點(diǎn)擊即可使用 jira-dev-tool

問題

安裝 jira-dev-tool(imooc-jira-tool)后啟動(dòng)項(xiàng)目聯(lián)調(diào)可能會(huì)出現(xiàn)的問題

問題一
  • 報(bào)錯(cuò):
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 已經(jīng)兩年沒有更新了,且依賴 react@“^16.0.0”, 若要繼續(xù)使用,在 npm i 時(shí)會(huì)有如下報(bào)錯(cuò):

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 手動(dòng)安裝

解決方案二(推薦)

  • 使用 yarn 代替 npm i
使用
  • 開發(fā)者工具用 MSW 以 Service Worker 為原理實(shí)現(xiàn)了"分布式后端"
  • 后端邏輯處理后,以localStorage為數(shù)據(jù)庫進(jìn)行增刪改查操作
  • 瀏覽器上安裝了一個(gè)獨(dú)立的后端服務(wù)和數(shù)據(jù)庫,再也不受任何中心化服務(wù)的影響 點(diǎn)擊’清空數(shù)據(jù)庫’便可以重置后端服務(wù)
  • 可以精準(zhǔn)地控制 HTTP請求的時(shí)間、失敗概率、失敗規(guī)則
  • Service Worker + localStorage雖然本質(zhì)上與傳統(tǒng)后端服務(wù)并不同,但絲毫不會(huì)影響前端開發(fā)

其他具體操作可見文檔以及接下來的操作:jira-dev-tool - npm

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

安裝好后進(jìn)入/login,請求login接口,可以看到狀態(tài)碼后帶有(from service worker)字樣即成功連接:

【實(shí)戰(zhàn)】 JWT、用戶認(rèn)證與異步請求(1) —— React17+React Hook+TS4 最佳實(shí)踐,仿 Jira 企業(yè)級項(xiàng)目(四)

開發(fā)工具控制臺第一個(gè)tab頁設(shè)置請求最短時(shí)間、請求失敗比例:

【實(shí)戰(zhàn)】 JWT、用戶認(rèn)證與異步請求(1) —— React17+React Hook+TS4 最佳實(shí)踐,仿 Jira 企業(yè)級項(xiàng)目(四)

開發(fā)工具控制臺將/login添加到異步請求失敗設(shè)置中,狀態(tài)碼 400 變?yōu)?500,提示:“請求失敗,請檢查 jira-dev-tool 的設(shè)置”:
【實(shí)戰(zhàn)】 JWT、用戶認(rèn)證與異步請求(1) —— React17+React Hook+TS4 最佳實(shí)踐,仿 Jira 企業(yè)級項(xiàng)目(四)

4.JWT原理與auth-provider實(shí)現(xiàn)

注冊一個(gè)新用戶
  • 修改:src\screens\login\index.tsx
    • 調(diào)用接口 login 改為 register;
    • 按鈕 登錄 改為 注冊

注冊一個(gè)新用戶 jira(密碼:jira),接口返回:

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

token 即是 JWT(JSON Web Tokens) 的產(chǎn)物

  • 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

模擬第三方服務(wù)

// 在真實(shí)環(huán)境中,如果使用了 firebase 這種第三方 auth 服務(wù)的話,本文件不需要開發(fā)者開發(fā)

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)

細(xì)節(jié)點(diǎn):

  • 函數(shù)定義時(shí),值前添加 async 使其返回一個(gè) Promise 對象
  • 回調(diào)函數(shù)入?yún)⒑突卣{(diào)函數(shù)內(nèi)有且只有一個(gè)函數(shù)調(diào)用且它的入?yún)⑴c回調(diào)函數(shù)入?yún)⒁恢?,該回調(diào)函數(shù)可直接簡寫為其內(nèi)部的函數(shù)調(diào)用且不帶參(這是函數(shù)式編程-PointFree的一種應(yīng)用):
    • const login = (form: AuthForm) => auth.login(form).then(user => setUser(user))
    • const login = (form: AuthForm) => auth.login(form).then(setUser)

【筆記】函數(shù)式編程——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}) => {
  // 這里要考慮到初始值的類型與后續(xù)值類型,取并組成一個(gè)泛型
  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>
}

在項(xiàng)目中使用 AppProvider,修改 src\index.tsx

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

修改 src\screens\login\index.tsx,調(diào)用 useAuth 中的 login,并使用之前注冊的賬號 jira(jira) 驗(yàn)證:

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

export const Login = () => {
  const {login, user} = useAuth()
  // HTMLFormElement extends Element (子類型繼承性兼容所有父類型)(鴨子類型:duck typing: 面向接口編程 而非 面向?qū)ο缶幊?
  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.zghlxwxcb.cn/news/detail-498416.html

到了這里,關(guān)于【實(shí)戰(zhàn)】 JWT、用戶認(rèn)證與異步請求(1) —— React17+React Hook+TS4 最佳實(shí)踐,仿 Jira 企業(yè)級項(xiàng)目(四)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包