赞
踩
相对原教程,我在学习开始时(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 |
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 |
具体配置、操作和内容会有差异,“坑”也会有所不同。。。
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 接口
__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.json
中 json-server
的 script
:"json-server": "json-server __json_server_mock__/db.json -w -p 3001 --middlewares ./__json_server_mock__/middleware.js"
json-server
,输入中间件预置用户名和密码即可正常访问(200),否则(400:bad request)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
解决方案一:
"jira-dev-tool": "^1.7.61",
部分,jira-dev-tool 手动安装解决方案二(推荐)
yarn
代替 npm i
其他具体操作可见文档以及接下来的操作:jira-dev-tool - npm
安装好后进入/login
,请求login接口,可以看到状态码后带有(from service worker)字样即成功连接:
开发工具控制台第一个tab页设置请求最短时间、请求失败比例:
开发工具控制台将/login
添加到异步请求失败设置中,状态码 400 变为 500,提示:“请求失败,请检查 jira-dev-tool 的设置”:
src\screens\login\index.tsx
:
login
改为 register
;注册一个新用户 jira(密码:jira),接口返回:
{
"user": {
"id": 2087569429,
"name": "jira",
"token": "MjA4NzU2OTQyOQ=="
}
}
token 即是 JWT(JSON Web Tokens) 的产物
修改 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)
细节点:
新建 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>
);
};
部分引用笔记还在草稿阶段,敬请期待。。。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。