什么是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停止它。
应用示例
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));
fetch不反对设置超时,须要应用promise
function fetchTimeout(url, init, timeout = 3000) { return new Promise((resolve, reject) => { fetch(url, init) .then(resolve) .catch(reject); setTimeout(reject, timeout); })}
终止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); } });}