爪哇学习笔记——AJAX&Fetch API

什么是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 event xhr.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 request xhr.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 fetch(url[, option]);

参数
  • url:URL字符串或者一个Request对象
  • 【爪哇学习笔记——AJAX&Fetch API】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); } }); }

    推荐阅读