什么是AJAX?

AJAX是异步的JavaScript和XML(Asynchronous JavaScript And XML)。简略点说,就是应用 XMLHttpRequest 对象与服务器通信。 它能够应用JSON,XML,HTML和text文本等格局发送和接收数据。AJAX最吸引人的就是它的“异步”个性,也就是说它能够在不从新刷新页面的状况下与服务器通信,替换数据,或更新页面。

通过交互式网站和古代 Web 规范,AJAX正在逐步被 JavaScript 框架中的函数和官网的 Fetch API 规范取代。

XMLHTTPRequest

XMLHttpRequest(XHR)对象用于与服务器交互。通过 XMLHttpRequest 能够在不刷新页面的状况下申请特定 URL,获取数据。这容许网页在不影响用户操作的状况下,更新页面的部分内容。XMLHttpRequest在AJAX编程中被大量应用。
let xhr = new XMLHttpRequest();xhr.open('GET', 'http://domain/service');// request state change eventxhr.onreadystatechange = function () {    // request completed?    if (xhr.readyState !== 4) return;    if (xhr.status === 200) {        // request successful - show response        console.log(xhr.responseText);    } else {        // request error        console.log('HTTP error', xhr.status, xhr.statusText);    }};// xhr.timeout = 3000; // 3 seconds// xhr.ontimeout = () => console.log('timeout', xhr.responseURL);// progress事件能够报告长时间运行的文件上传// xhr.upload.onprogress = p => {//     console.log(Math.round((p.loaded / p.total) * 100) + '%');// }// start requestxhr.send();

全副readyState状态值都在 XMLHTTPRequest.readyState,如下也是:

  • 0 UNSENT Client has been created. open() not called yet.
  • 1 OPENED open() has been called.
  • 2 HEADERS_RECEIVED send() has been called, and headers and status are available.(服务器已收到申请)
  • 3 LOADING Downloading; responseText holds partial data.(服务器正在解决申请)
  • 4 DONE The operation is complete.

Fetch API

语法

Promise<Response> fetch(url[, option]);

参数

  • url:URL字符串或者一个Request对象
  • option可选,一个配置项对象

    • method: 申请应用的办法,如 GET、POST。
    • headers:申请头信息
    • body:申请的body信息
    • mode: 申请的模式,如 cors、 no-cors 或者 same-origin。
    • credentials: 申请的 credentials,如 omit、same-origin 或者 include。为了在以后域名内主动发送 cookie , 必须提供这个选项。因为默认是不携带cookie的
    • cache: 申请的 cache 模式: default、 no-store、 reload 、 no-cache 、 force-cache 或者 only-if-cached。
    • signal:一个AbortSignal对象实例,容许您与fetch申请进行通信,并在须要时通过AbortController停止它。

应用示例

  1. HTTP谬误(例如404 Page Not Found 或 500 Internal Server Error)不会导致Fetch返回的Promise标记为reject,从而catch办法也不会被执行。想要准确的判断 fetch是否胜利,须要蕴含 promise resolved 的状况,此时再判断 response.ok是不是为 true

    fetch('http://domain/service', {    method: 'GET'    })    .then(response => {        if (response.ok) {            return response.json();        }        throw new Error('Network response was not ok.');    })    .then(json => console.log(json))    .catch(error => console.error('error:', error));
  2. fetch不反对设置超时,须要应用promise

    function fetchTimeout(url, init, timeout = 3000) {    return new Promise((resolve, reject) => {        fetch(url, init)            .then(resolve)            .catch(reject);        setTimeout(reject, timeout);    })}
  3. 终止fetch

    const controller = new AbortController();fetch(        'http://domain/service', {            method: 'GET',            signal: controller.signal        })    .then(response => response.json())    .then(json => console.log(json))    .catch(error => console.error('Error:', error));controller.abort();

留神
xhr的回调是宏工作,fetch基于promise实现,其回调是微工作

常见HTTP申请头/响应头/状态码

request header

  • :method: GET
  • :path:
  • :scheme: https
  • accept: application/json, text/plain, /
  • accept-encoding: gzip, deflate, br
  • cache-control: no-cache
  • cookie: deviceId=c12;
  • origin:
  • referer:
  • user-agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1

response header

  • access-control-allow-credentials: true
  • access-control-allow-origin:
  • content-encoding: gzip
  • content-type: application/json;charset=UTF-8
  • date: Thu, 06 Aug 2020 08:15:05 GMT
  • set-cookie: sess=QvrAQ0Cq+EcDQQPTer2X;
  • status: 200

status

  • 200 get 胜利
  • 201 post 胜利
  • 301 永恒重定向
  • 302 长期重定向
  • 304 协商缓存 服务器文件未修改
  • 400 客户端申请有语法错误,不能被服务器辨认
  • 403 服务器受到申请,然而回绝提供服务,可能是跨域
  • 404 申请的资源不存在
  • 405 申请的method不容许
  • 500 服务器产生不可预期的谬误

封装一个跨浏览器兼容的申请函数

interface IOptions {    url: string;    type?: "GET" | "POST";    data: any;    timeout?: number;}function formatUrl(object) {    // a=xxx&b=xxxx; querystring    let dataArr = [];    for (let key in object) {        dataArr.push(`${key}=${encodeURIComponent(object[key])}`);    }    return dataArr.join("&");}export function ajax(    options: IOptions = {        type: "GET",        data: {},        timeout: 3000,        url: "",    }) {    return new Promise((resolve, reject) => {        if (!options.url) {            return;        }        const queryString = formatUrl(options.data);        const onStateChange = () => {            xhr.onreadystatechange = () => {                if (xhr.readyState === 4) {                    clearTimeout(timer);                    if (                        (xhr.status >= 200 && xhr.status < 300) ||                        xhr.status === 304                    ) {                        resolve(xhr.responseText);                    } else {                        reject(xhr.status);                    }                }            };        };        let timer;        let xhr;        if ((window as any).XMLHttpRequest) {            xhr = new XMLHttpRequest();        } else {            xhr = new ActiveXObject("Microsoft.XMLHTTP");        }        if (options.type.toUpperCase() === "GET") {            xhr.open("GET", `${options.url}?${queryString}`);            onStateChange();            xhr.send();        } else if (options.type.toUpperCase() === "POST") {            xhr.open("POST", options.url);            xhr.setRequestHeader(                "ContentType",                "application/x-www-form-urlencoded"            );            onStateChange();            xhr.send(options.data);        }        if (options.timeout) {            timer = setTimeout(() => {                xhr.abort();                reject("timeout");            }, options.timeout);        }    });}