ajax

32 阅读4分钟

向服务器发送数据请求的方案:

   第一类:XMLHttpRequest
     + ajax:自己编写请求的逻辑和步骤
     + axios:第三方库,对XMLHttpRequest进行封装「基于promise进行封装」
   第二类:fetch
     ES6内置的API,本身即使基于promise,用全新的方案实现客户端和服务器端的数据请求
     + 不兼容IE
     + 机制的完善度上,还是不如XMLHttpRequest的「例如:无法设置超时时间、没有内置的请求中断的处理...」
   第三类:其它方案,主要是跨域为主
     + jsonp          利用script标签,不存在跨域问题
     + postMessage
     + 利用img的src发送请求,实现数据埋点和上报!!
     + ...

fetch

let promise实例(p) = fetch(请求地址,配置项);
   + 当请求成功,p的状态是fulfilled,值是请求回来的内容;如果请求失败,p的状态是rejected,值是失败原因!
   + fetch和axios有一个不一样的地方:
     + 在fetch中,只要服务器有反馈信息(不论HTTP状态码是多少),都说明网络请求成功,最后的实例p都是fulfilled,只有服务器没有任何反馈(例如:请求中断、请求超时、断网等),实例p才是rejected!
     + 在axios中,只有返回的状态码是以2开始的,才会让实例是成功态! 

** 配置项:**

 配置项:
     + method 请求的方式,默认是GETGETHEADDELETEOPTIONSPOSTPUTPATCH;」
     + cache 缓存模式「*default, no-cache, reload, force-cache, only-if-cached」
     + credentials 资源凭证(例如cookie)「include, *same-origin, omit」
       fetch默认情况下,跨域请求中,是不允许携带资源凭证的,只有同源下才允许!!
       
       资源凭证(例如cookie)在客户端和服务端通信中,只要客户端设置了cookie,每次请求时,默认就会在请求头中,基于cookie字段,把本地设置的cookie信息,传递给服务器
       
       include:同源和跨域下都可以
       same-origin:只有同源才可以
       omit:都不可以
     + headers:普通对象{}/Headers实例
       自定义请求头信息
     + body:设置请求主体信息
       + 只适用于POST系列请求,在GET系列请求中设置body会报错{让返回的实例变为失败态}
       + body内容的格式是有要求的,并且需要指定 Content-Type 请求头信息
         + JSON字符串  application/json
           '{"name":"xxx","age":14,...}'
         + URLencodeed字符串  application/x-www-form-urlencoded
           'xxx=xxx&xxx=xxx'
         + 普通字符串  text/plain
         + FormData对象  multipart/form-data
           主要运用在文件上传(或者表单提交)的操作中!
           let fm=new FormData();
           fm.append('file',文件);
           ...
         + 二进制或者Buffer等格式
         + ...
     + 我们发现,相比较于axios来讲,fetch没有对GET系列请求,问号传参的信息做特殊的处理(axios中基于params设置问号参数信息),需要自己手动拼接到URL的末尾才可以!!
     
     
 document.body.addEventListener('click', function () {
    fetch('/api/addTask', {
        method: 'POST',
        // 设置请求头
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        // 自己手动把请求主体格式变为服务器需要的
        body: qs.stringify({
            task: '我学会了Fetch操作',
            time: '2022-12-15 12:00:00'
        })
    }).then(response => {
        let { status, statusText } = response;
        if (/^(2|3)\d{2}$/.test(status)) {
            return response.json();
        }
        return Promise.reject({
            code: -100,
            status,
            statusText
        });
    }).then(value => {
        console.log('最终处理后的结果:', value);
    }).catch(reason => {
        message.error('请求失败,请稍后再试~~');
    });
}); 

Headers类和普通对象



1.Headers对象
 headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },

2.Headers类:头处理类「请求头或者响应头」 
  Headers.prototype
    + append 新增头信息 
    + delete 删除头信息
    + forEach 迭代获取所有头信息
    + get 获取某一项的信息
    + has 验证是否包含某一项
    + ...

let head = new Headers;
head.append('Content-Type', 'application/json');
head.append('name', 'zhufeng'); 

let p = fetch('/api/getTaskList?state=2', {
    headers: head
});

Snipaste_2023-10-30_15-54-17.png

服务器返回的response对象「Response类的实例」

 服务器返回的response对象「Response类的实例」
   私有属性:
     + body 响应主体信息「它是一个ReadableStream可读流」
     + headers 响应头信息「它是Headers类的实例」
     + status/statusText 返回的HTTP状态码及描述
     Response.prototype
     + arrayBuffer
     + blob
     + formData
     + json
     + text
     + ...
     这些方法就是用来处理body可读流信息的,把可读流信息转换为我们自己需要的格式!!
     返回值是一个promise实例,这样可以避免,服务器返回的信息在转换中出现问题(例如:服务器返回的是一个流信息,我们转换为json对象肯定是不对的,此时可以让其返回失败的实例即可)

get

let p = fetch('/api/getTaskList?state=2', {
    headers: head
});
p.then(response => {
    // 进入THEN中的时候,不一定是请求成功「因为状态码可能是各种情况」
    //解构
    let { headers, status, statusText } = response;
    if (/^(2|3)\d{2}$/.test(status)) {
        // console.log('成功:', response);
        // console.log('服务器时间:', headers.get('Date'));
        //把可读流信息转换为我们自己需要的格式,  返回值是一个promise实例
        return response.json();
    }
    // 获取数据失败的「状态码不对」
    return Promise.reject({
        code: -100,
        status,
        statusText
    });
}).then(value => {
    console.log('最终处理后的结果:', value);
}).catch(reason => {
    // 会有不同的失败情况
    // 1.服务器没有返回任何的信息
    // 2.状态码不对
    // 3.数据转换失败
    // ....
    console.log('失败:', reason);
}); 

post

import { message } from 'antd'
import qs from 'qs'  
//qs库把对象变为 URLENCODED字符串  application/x-www-form-urlencoded,传给服务器

document.body.addEventListener('click', function () {
    fetch('/api/addTask', {
        method: 'POST',
        // 设置请求头
        axios中我们设置普通对象,axios内部会把它变为json字符串传给服务器,并且自己设置请求头中的Content-Type, **fetch需要自己做**
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        // 自己手动把请求主体格式变为服务器需要的
        body: qs.stringify({
            task: '我学会了Fetch操作',
            time: '2022-12-15 12:00:00'
        })
    }).then(response => {
        let { status, statusText } = response;
        if (/^(2|3)\d{2}$/.test(status)) {
            return response.json();
        }
        return Promise.reject({
            code: -100,
            status,
            statusText
        });
    }).then(value => {
        console.log('最终处理后的结果:', value);
    }).catch(reason => {
        message.error('请求失败,请稍后再试~~');
    });
});

fetch中的请求中断 ,本身没有请求中断,需要依托AbortController

[MDN](AbortController - Web API 接口参考 | MDN (mozilla.org))

let ctrol = new AbortController();
fetch('/api/getTaskList', {
    // 请求中断的信号
    signal: ctrol.signal
}).then(response => {
    let { status, statusText } = response;
    if (/^(2|3)\d{2}$/.test(status)) return response.json();
    return Promise.reject({
        code: -100,
        status,
        statusText
    });
}).then(value => {
    console.log('最终处理后的结果:', value);
}).catch(reason => {
    // {code: 20,message: "The user aborted a request.", name: "AbortError"}
    console.dir(reason);
});
// 立即中断请求
 ctrol.abort();