ajax 请求的搭建
- 引入mock
- AP接口设计
- AJAX 通讯
前置知识
- HTTP 协议 , 前后端通讯的桥梁
- API : XMLHttpRequest 和 fetch
- 常用工具axios
mock 引入
使用 mockJS
- 前端代码中引入 mockJs
- 定义要模拟的路由 , 返回结果
- mockJs 劫持ajax请求(返回模拟的结果)
import Mock from 'mockjs'
Mock.mock('/api/test', 'get', ()=>{
return {
code: 0,
data: {
name:"lxy text"
}
}
})
使用fetch api 向后端发起请求
useEffect(()=>{
fetch('/api/test')
.then((res)=>{
console.log("res = ",res)
})
.then((err)=>{
console.log("err = ",err)
})
},[])
bug : 发现返回的数据不是我们模拟的
mockjs 劫持失败
因为mock JS 只能劫持XMLHttpRequest
使用axios(要先安装哦) axios中文文档|axios中文网 | axios (axios-js.com)
axios.get('/api/test')
.then(function (response) {
console.log(response.data.data);
})
.catch(function (error) {
console.log(error);
});
成功
总结
- 只能劫持XMLHttpRequest 不能劫持fetch ,有局限性
- 注意线上环境要注释掉,否则线上请求也被劫持
前端项目中不建议使用 mock JS
node JS + mock JS
将mockJS 用于nodeJS服务端 , 使用它的Random能力
后端操作
初始化node 环境 npm init -y
安装mock JS
安装nodemon
自定义启动命令
安装 koa
Koa (koajs) – 基于 Node.js 平台的下一代 web 开发框架 | Koajs 中文文档 (bootcss.com)
这里添加异步函数模拟请求响应的时间差
const Mock = require('mockjs');
const Random = Mock.Random;
module.exports = [
{
url: '/api/question/:id',
method: 'get',
response: () => {
return {
code: 200,
data: {
id: Random.id(),
title: Random.ctitle()
}
}
}
},
{
url: '/api/question',
method: 'post',
response: () => {
return {
code: 200,
data: {
id: Random.id(),
name: Random.cname(),
}
}
}
}
]
const Koa = require('koa');
const Router = require('koa-router');
const mockList = require('./mock/index');
const app = new Koa();
const router = new Router();
//定义异步函数
async function getRes(fn) {
return new Promise(resolve => {
setTimeout(() => {
const res= fn()
resolve(res)
}, 2000)
})
}
//注册 mock 路由
mockList.forEach(item => {
const {url , method , response} = item;
router[method](url, async ctx => {
// const res=response();
//模拟网络请求的加载状态, 2S
const res = await getRes(response);
ctx.body = res;
})
})
app.use(router.routes());
app.listen(3001) // 监听的端口号
启动成功
localhost:3001/api/question/12
前端操作
useEffect(()=>{
// 跨域
// > 前端地址:http://localhost:3000
// > 后端地址:http://localhost:3001
fetch('http://localhost:3001/api/test')
.then((res)=>{
console.log("res = ",res)
})
.then((err)=>{
console.log("err = ",err)
})
},[])
跨域
前端地址:http://localhost:5173
后端地址:http://localhost:3001
发现还是报错
在后端改
在线mock平台
fast-mock y-api swagger
API 设计
用户API
- 登录
- 注册
- 获取用户信息
问卷api
- 创建问卷
- 获取单个问卷
- 更新问卷
- 删除问卷
- 查询问卷
- 复制问卷
使用Restful API
method: ,
path: ,
request body: ,
responde: ,
用户验证
JWT
统一返回格式
errno , data ,msg
实战
配置axios 基本功能
- 创建axios实例
- 配置全局的拦截器
import { message } from "antd";
import axios from "axios";
//1.创建实例
const instance = axios.create({
baseURL: 'http://localhost:3001/api/',
timeout: 1000,//等待一秒
headers: {'X-Custom-Header': 'foobar'}
});
//2.添加请求拦截器
instance.interceptors.request.use(function () {
// 在发送请求之前做些什么
console.log("我要发请求啦");
}, function () {
// 对请求错误做些什么
console.log("请求错误啦");
});
//3.添加响应拦截器
instance.interceptors.response.use(function (res) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
console.log("我收到响应啦");
const resData = (res.data || {}) as ResType;
const {errno,data,msg} = resData;
if(errno !== 0){
message.error(msg || "未知错误");
}
return data as any;
}, function () {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
console.log("响应错误啦");
});
//定义类型
type ResType={
errno:number,
data?:ResDataType,
msg?:string
}
type ResDataType={
[keu:string]: any //可以有任意值,只要key键是string类型
}
export default instance ;
模拟axios请求
请求函数
import axios , {ResDataType} from "./axios"
//获取单个问卷
export async function getQuestinService(id: string): Promise<ResDataType>{
const url=`/question/${id}`
const data = ( await axios.get(url) ) as ResDataType;
return data;
}
使用
import React,{FC,useEffect} from 'react'
import {useParams} from 'react-router-dom';
//导入发起请求的函数
import { getQuestinService } from '../../../services/question';
const Edit : FC = ()=>{
//获取携带的参数
const {id = ''} = useParams();
useEffect(()=>{
getQuestinService(id);
},[])
return (
<>
<h1>edit {id}</h1>
{/* http://localhost:5173/question/edit/20 */}
</>
)
}
export default Edit;
报错
TypeError: Cannot read properties of undefined (reading ‘cancelToken’)
TypeError: Cannot read properties of undefined (reading ‘cancelToken‘)_cannot read properties of undefined (reading 'canc-CSDN博客
又报错
message: ‘timeout of 1000ms exceeded’
原来是前端设置了等待一秒,改一下
timeout: 1000 * 10,//等待10秒
页面添加loading效果
自定义
function useLoadQuestionData() {
const {id = ''} =useParams()
const [loading,setLoading] = useState(true)
const [questionData,setQuestionData] = useState({})
useEffect(()=>{
async function fn()
{
const data = await getQuestinService(id)
setQuestionData(data)
setLoading(false)
}
fn()
},[])
return {loading,questionData}
}
使用ahooks中的useRequest
async function load(){
const data = await getQuestinService(id)
return data;
}
const {loading,data,error} =useRequest(load)
return {loading,data,error}
useRequest 与 自定义发请求
自定义请求
const[questionList,setQuestionList] = useState([])
const [total ,setTotal] =useState(0)
useEffect(()=>{
async function fn()
{
//问卷列表数据模拟
const data= await getQuestinListService()
const {list=[],total=0} =data
setQuestionList(list)
setTotal(total)
}
fn()
},[])
使用useRequest
const {data={},loading} = useRequest(getQuestinListService)
const {list=[],total=0} = data
列表增加搜索hook
向后端发起请求的接口
//获取(搜索)问卷列表
export async function getQuestinListService(
opt:Partial<SearchType>
): Promise<ResDataType>{
const url='/question'
const data = ( await axios.get(url,{
params:opt
}) ) as ResDataType;
return data;
}
自定义hook
import {LIST_SEARCH_PARAM_KEY} from "../constant/index";
import { useSearchParams } from "react-router-dom";
import { useRequest } from "ahooks";
//导入发起请求的函数
import { getQuestinListService } from "../services/question";
function useLoadQuestionData() {
const [searchParams] = useSearchParams();
async function load(){
const keyword=searchParams.get(LIST_SEARCH_PARAM_KEY) || " "
const data = await getQuestinListService({keyword});
return data;
}
const {loading,data,error} =useRequest(load,{
refreshDeps:[searchParams],//刷新的依赖项
})
return {loading,data,error}
}
export default useLoadQuestionData;
使用自定义hook重构list,Star,Trash页面,向后端发请求
发现星标页面并未实现真的搜索功能
因为后端是随机生成的
解决