0.0、叠甲
- 本人水平有限,文章如果有错误希望大家能帮我指出,共同进步
- 本文只做简单了解,既是写作也是为了加深我自己的记忆
1.0、远古时代的网络请求
- 用户发起请求,客户端堵塞,必须等到
请求响应返回对应的html后,刷新页面
2.0、原生Ajax(XMLHttpRequest)
2.1、什么是ajax请求?
Ajax,Asynchronous JavaScript + XML,既不需要刷新整个页面得到新的结果XML是一种数据传递格式,现在大多使用json,例如axios默认支持json
2.2、基础的ajax请求
-
Ajax主要是利用浏览器中内置的
XMLHttpRequest -
可以看看下面这张图分析一下,
XML中的readyState在不同时候分别为几? -
带参请求一般是通过拼接url实现的:"url"+paramsvar xhr = new XMLHttpRequest();//一个新的 XMLHttpRequest 对象。在调用 send ()向服务器发送请求之前,必须至少调用 open ()来初始化对象。 console.log("xhr0",xhr); //Open ()方法已被调用。在此状态下,可以使用 setRequestHeader ()方法设置请求标头,第三个参数为是否异步请求 xhr.open('GET','https://www.fastmock.site/mock/59015bc116fee0e86228e4f540fbfcd3/internet/index',true) // Send ()已被调用,响应标头已被接收。 xhr.send() // 只要xhr的readystate发生变化就会触发onreadystatechange事件 xhr.onreadystatechange = function(){ console.log("xhr",xhr); if(xhr.readyState === 4 && xhr.status === 200){ console.log("xhr",xhr); } } -
xhr的其他实例方法参考: XHR MDN文档
3.0、Jquery封装的ajax(XMLHttpRequest)
3.1、什么是Jquery中的ajax?
- 封装好的xhr,我们只需要一个
$.({})即可,和Jquery操作dom一样简单
3.2、Jquery Ajax的使用
-
封装了
xhr的使用,只需要关心配置$.ajax({ type: 'GET', url: "https://www.fastmock.site/mock/59015bc116fee0e86228e4f540fbfcd3/internet/index", success: function(res){console.log("res",res)}, error: function(result) {console.log("err",result)} }) $.ajax({ type: 'GET', url: "https://www.fastmock.site/mock/59015bc116fee0e86228e4f540fbfcd3/internet/indexOOXX", success: function(res){console.log("res",res)}, error: function(result) {console.log("err",result)} }) -
缺点在于因为
jquery的包庞大,并且esmodule时代的来临,让我们越来越不可能为了一个jquery封装的ajax引入整个jquery -
JqueryAjax其他参数参考:JqueryAjax
4.0、Fetch
-
fetch是一种新的获取接口资源的方式,是为了超越XHR而诞生的新Api -
fetch优点在于浏览器原生支持,但是也有对老浏览器不兼容的问题 -
fetch是基于promise实现的fetch('https://www.fastmock.site/mock/59015bc116fee0e86228e4f540fbfcd3/internet/index') .then(response=>{ //Response 对象并不直接包含实际的 JSON 响应体,而是表示整个 HTTP 响应。 console.log("response",response)//可以看到fetch返回的是一个stream对象 return response.json() }) .then(response=>console.log("res",response))// MDN fetch例子 async function postData(url = '', data = {}) { // Default options are marked with * const response = await fetch(url, { method: 'POST', // *GET, POST, PUT, DELETE, etc. mode: 'cors', // no-cors, *cors, same-origin cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached credentials: 'same-origin', // include, *same-origin, omit headers: { 'Content-Type': 'application/json' // 'Content-Type': 'application/x-www-form-urlencoded', }, redirect: 'follow', // manual, *follow, error referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url body: JSON.stringify(data) // body data type must match "Content-Type" header }); return response.json(); // parses JSON response into native JavaScript objects } postData('https://example.com/answer', { answer: 42 }) .then(data => { console.log(data); // JSON data parsed by `data.json()` call });
5.0、Axios(XMLHttpRequest / promise)
5.1、什么是axios
Axios是一个基于promise的HTTP库(可以先看8.0),可以用在浏览器和node.js中- 在
node中创建http请求,在浏览器中创建XHR对象 - 自动转换
JSON数据 - 客户端支持防御XSRF
5.2、axios的使用
- 来看看官方文档的写法
//官方写法 axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone' } }); //简写(我还是喜欢简写的):带参get请求 export const searchFruit = (props:any) => { return baseAxios.get('fruit/table/sku/search', { params:{ plantCode:props.plantCode, searchTxt:props.searchTxt, pageSize:props.pageSize, pageIndex:props.pageIndex, }, } ) } //封装请求域名,便于根据测试/正式快速切换 const API_PATH = 'https://www.fastmock.site/mock/59015bc116fee0e86228e4f540fbfcd3/internet' export const baseAxios = axios.create({ baseURL: API_PATH })
5.3、axios响应器/拦截器
- 响应器和拦截器就像中间件一样,在请求前把需要的token带上,在响应时把错误抛出等等情况
// 添加请求拦截器 baseAxios.interceptors.request.use(function (config) { // 在发送请求之前做些什么 const {token} = useTokenState.getState().state; config.headers={ Authorization: "Bearer "+token } return config; }, function (error) { // 对请求错误做些什么 console.log("error",error) }); // 添加响应拦截器 baseAxios.interceptors.response.use((response)=>{ // 2xx 范围内的状态码都会触发该函数。 return response; }, function (error) { if (error.response.status) { //在这里我们return出error的错误,以便于在接口层级拿到报错信息 switch(error.response.status){ case 400: return Promise.reject(error.response); case 404: showAlert("404请求的资源无法找到!") return Promise.reject(error.response); case 500: showAlert("服务器错误!") return Promise.reject(error.response); case 502: showAlert("服务器错误!") return Promise.reject(error.response); } ... } });
6.0、基础小知识:get&post参数位置
- 一般来说,大家都觉得
get请求参数要放在parmas里,post请求参数要放在body里//以axios简写方式为例 //get请求参数放在params里,即url里 axios.get('https://www.fastmock.site/mock/ed4cc5916cf80930a5aa157ffb1686f7/bookstore/books',{ params:{ name: "Youngzx", id: "NBSP12345678s" } }).then(res=>{ console.log("res",res.data) }) //post请求参数放在请求体里 return baseAxios.post('/pumpkin/mobile/order/update',{ orderId: orderId, type: type, item: item }) - 实际上放哪里根本没有区别,他们都是通过
TCP连接,给Get请求的参数放在Body里,和POST用Params对于后端来说没有区别,只是为了区分不同的请求,而定义的规则,遵守可以让我们的代码看起来更加规范 - 有的人说
GET比POST性能好,因为浏览器只会发送一个数据包,而浏览器发送POST请求会先发起一条option请求,但实际上GET请求会被多次缓存,而POST不会,所以在了解GET与POST本质没有区别的情况下,还是需要合理的运用GET与POST(有的公司只用get,有的只用post)
7.0、基础小知识:formdata 和 json
作为一个小废物,我一开始确实没了解formdata的兼容问题,后面才发现formdata也是有些许兼容问题的(见7.3)- 网络请求无非是前端向后端传输对应的数据,后端解析数据,并返回正确的内容给前端
- 那么数据的格式就显得十分重要,理论上来说
formdata和json不过是格式不同,你有能力可以自己造一种更高效的数据传输格式,只要后端能解析即可
7.1、区别
formdata一般是通过表单提交,FormData提供了一种表示表单数据的键值对key/value的构造方式//formdata在node里没有,可以在浏览器控制台输入,或者import formdata var formdata = new FormData(); formdata.append("username","Youngzx") formdata.append("pwd",12345678) form.forEach((key, val) => { console.log(key, val) //Youngzx username //12345678 pwd } })- json是一个序列化的
对象或数组 - json 与 js 对象的关系:
json是js对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串//js对象 const obj = {name: 'Youngzx',age:18} //json const json = '{"name" : "Youngzx" , "age" : "18"}'
7.1、json
ajax传输jsonlet data = { name : "Youngzx", age : 18 } console.log(JSON.stringify(data)) $.ajax({ url: 'https://www.fastmock.site/mock/ed4cc5916cf80930a5aa157ffb1686f7/bookstore/books', dataType: 'json', type: 'get', //`data`参数需要使用`JSON.stringify()`方法序列化成JSON字符串 data: JSON.stringify(data), //`contentType`需要指定为`'application/json;charset=utf-8'` contentType: 'application/json;charset=utf-8', success: function (res) { console.log(res); } });axios默认支持传输json,不需要我们进行转化
7.2、formdata
-
ajxa传输formdata -
axios传输formdata(post请求)//qs将data转为formdata,在第三个参数中标记请求头为'application/x-www-form-urlencoded' export const login = (data:any) => { data = qs.stringify(data) return baseAxios.post('/auth-center/oauth/token?',data,{headers: { 'content-type': 'application/x-www-form-urlencoded' }}) }
7.3、构建formdata的方式
7.1中的FormData对象,自己append出一个格式正确的formdata- 使用
qsimport qs from 'qs' const formdata = { name: "youngzx", age: 18, value: 200 } const data = qs.stringify(formdata)//name=youngzx&age=18&value=200 - 表单提交
URLSearchParams(有兼容问题,老浏览器不支持)
8.0、基础小知识:Promise
8.1、前情提要
- 在Js里,所有代码都是单线程执行的
// 所以我们的网络请求需要异步执行 function getInfo(){ console.log("This is the data I want to get after three seconds") } setTimeout(() => { getInfo() },3000) function getValue(){ console.log("I want to execute one second after getInfo executes") } // 如果我想要在getInfo后去执行getValue函数 setTimeout(() => { getInfo() setTimeout(() => { getValue() },1000) },3000) - promise的定义是以后承诺在未来某个时间给我们对应的结果,例如.then以后,async/await以后
- xhr也是如此,当readystate === 4的时候触发回调函数
- promise的出现是正是为了解决回调地狱,使得回调的写法变为同步的写法
8.2、promise的使用
- promise状态:pending(进行中),fullfilled(已成功),rejected(已失败)
- pormise构造函数接受resolve和reject两个函数作为参数,reslove()会把pending状态转变为fullfilled
const promise1 = new Promise((resolve, reject) => { resolve(3) }); console.log("promise1",promise1)//promise1 Promise { 3 } - promise的实例可以用.then()中的两个参数指定resolve和reject的回调
const promise1 = new Promise((resolve, reject) => { setTimeout(()=>{ resolve(777) },5000) }); //5秒一到会触发.then的回调,把promise2的pending状态变为fullfilled状态 promise1.then(resolve=>{ console.log(resolve)//在5秒钟后得到777 },reject=>{ console.log(reject)//reject同理 }) //所以我们日常写请求的时候一般是这样得到接口的返回值的,用了async await语法糖会更加简单 const res = axios.get("urlxxx").then(res=>{return res}) - promise还有许多api例如常用的
promise.all,详见:Promise MDN
9.0、基础小知识、语法糖async/await
- 被async包裹的函数会被当作一个promise
- await则是.then()得到resolve或者reject以后的内容
- async/await的出现让代码看起来更像同步代码
//没有asycn/await的写法 function getXXX() { const p1 = new Promise((resolve,reject) => { setTimeout(()=>{ resolve(300) },3000) }) return p1; } p1.then(res=>{console.log("Res",res)}) //有async/await的写法 async function haveAsyncAwait () { const res = await getXXX(200) console.log(res) // } haveAsyncAwait()
10.0、总结
xhr虽然旧,但是仍然好用,例如axios- 原生的
xhr和jquery封装的xhr过于笨重,耦合过多 fetch虽然新,但是需要自己封装的东西还是太多,而且老浏览器不一定适用- 现阶段无脑选择
axios即可,配合promiseyyds