XMLHttpRequest 为 ajax 的核心。
var xhr = new XMLHttpRequest()
本质为一个函数;
typeof XMLHttpRequest ; // 'function'
原型链关系:XMLHttpRequest “继承”于 XMLHttpRequestEventTarget,XMLHttpRequestEventTarget “继承”于 EventTarget ;
var xhr = new XMLHttpRequest();xhr instanceof XMLHttpRequest; // truexhr instanceof XMLHttpRequestEventTarget ; // truexhr instanceof EventTarget ; // true
实例化时属性,xhr 上属性
事件绑定
onabort =》中止请求时触发
ontimeout =》超时触发
响应体相关
readyState =》初始值为0,在 new XMLHttpRequest() 的时候
responseXML
状态相关
status
statusText
其他
第一层原型,XMLHttpRequest 属性和方法
send(data?) =》发送请求
其他
设置了 xhr 的所有属性的 getter 和 setter
第二层原型,XMLHttpRequestEventTarget
仅设置了 xhr 的 事件绑定中的方法的 getter 和 setter
第三层原型,EventTarget
前端测试代码如下:
var xhr = new XMLHttpRequest()console.log(xhr.readyState); // 此时的 readyState 为0var eventKeys = [ 'abort', 'error', 'load', 'loadend', 'loadstart', 'progress', 'readystatechange', 'timeout']eventKeys.forEach(key => { xhr[`on${key}`] = function () { console.log(`-------request is ${key}-------`) console.log(`request is on${key}`) log() console.log('\n\n') }});xhr.open('get', 'http://localhost:9000/', true);xhr.send()function log() { console.log('readyState=', xhr.readyState); console.log('status=', xhr.status); console.log('statusText', xhr.statusText);}
nodejs 服务端代码如下:
const Koa = require('koa');const app = new Koa();function response() { return new Promise((resolve, reject) => { setTimeout(() => { resolve('hello world') }, 10000); })}app.use(async ctx => { ctx.set("Access-Control-Allow-Origin", "*"); ctx.set('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS'); ctx.set("Access-Control-Allow-Headers", "X-Requested-With"); ctx.set('Access-Control-Allow-Headers', 'Content-Type'); var res = await response() ctx.body = res;})app.listen(9000)
根据打印信息,成功的普通请求概括如下:
通过 readyState 看请求周期
var xhr = new XMLHttpRequest()xhr.onload = function () { console.log(xhr.response, xhr.responseText)}xhr.open('get', 'http://localhost:9000/', true)xhr.send();
var xhr = new XMLHttpRequest();xhr.onload = function () { console.log(xhr.response, xhr.responseText)}xhr.open('post', 'http://localhost:9000/', true)xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')xhr.send('name=tom&age=18');
设置请求的格式为
x-www-form-urlencoded
, 且传递的数据为字符串,用&
连接不同的字段。设置请求头,xhr.setRequestHeader( key,val )
var xhr = new XMLHttpRequest();xhr.onload = function () { console.log(xhr.response, xhr.responseText)}xhr.open('post', 'http://localhost:9000/', true)xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");xhr.send(JSON.stringify({ name: 'tom', age: 18 }));
var xhr = new XMLHttpRequest()xhr.ontimeout = function(){ console.log(xhr.status,xhr.readyState,xhr.responseText) console.log('请求超时')}xhr.onloadend = function(){ console.log(xhr.status,xhr.readyState,xhr.responseText) console.log('请求结束')}xhr.onreadystatechange =function(){ console.log('readyState change',xhr.readyState)}xhr.timeout = 2000xhr.open('get', 'http://localhost:9000/', true);xhr.send();
超出设置的 timeout 时间的请求,结束时,并不会触发 onerror 和 onabort 以及 onload ,只会触发 ontimeout 和 onloadend;
readyState 从 0-》1-》4
onload 和 onloadend 的区别是,onloadend 不管请求成功与否都会触发,而 onload 只有请求成功结束时触发。
客户端
var xhr = new XMLHttpRequest()xhr.onload = function () { console.log('请求成功')}xhr.open('post', 'http://localhost:9000/', true);xhr.setRequestHeader('X-user','tom')xhr.setRequestHeader('X-token','ACDFWE@123123')xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");xhr.send();
服务器端(nodejs),设置允许传递的请求头字段,多个字段用逗号隔开。
app.use(async ctx => { let origin = ctx.request.header.origin ctx.set("Access-Control-Allow-Origin", origin); ctx.set('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS'); ctx.set("Access-Control-Allow-Headers", "X-user,X-token,Content-Type"); var res = await response() ctx.body = res;})
跨越时,需要设置
Access-Control-Allow-Origin
为发送请求的客户端的域名。字段不区分大小写。
客户端设置 xhr 实体 withCredentials 属性为 true
var xhr = new XMLHttpRequest()xhr.onload = function () { console.log('请求成功')}xhr.open('post', 'http://localhost:9000/', true);xhr.withCredentials = true; // herexhr.send();
服务端,设置允许
app.use(async ctx => { let origin = ctx.request.header.origin ctx.set("Access-Control-Allow-Origin", origin); ctx.set('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS'); ctx.set("Access-Control-Allow-Headers", "x-user,x-token,content-type"); ctx.set("Access-Control-Allow-Credentials", true); // here var res = await response() ctx.body = res;})
效果则是:
而且此时跨域的 options 请求好像也不发了
var xhr = new XMLHttpRequest()xhr.open('post', 'http://localhost:9000/', true);xhr.send();setTimeout(() => { xhr.abort();}, 3000);
手动 abort 必须在请求完成(onloadend)之前。
周期中能触发的事件有:onreadystatechange ,onloadstart,onprogress,onabort,onloadend
var xhr = new XMLHttpRequest()xhr.onload = function(){ console.log(xhr.response)}xhr.open('post', 'http://localhost:9000/', true);xhr.overrideMimeType('text/plain; charset=x-user-defined');// 或// xhr.responseType = 'blob';xhr.send();
xhr.onprogress = updateProgress;xhr.upload.onprogress = updateProgress;function updateProgress(event) { if (event.lengthComputable) { var completedPercent = event.loaded / event.total; } }
// ajax 方法定义function ajax(options) { var { url, method, data, headers } = options; var lowerMethod = method.toLowerCase(); var _isGet = lowerMethod === 'get'; var _isPost = lowerMethod === 'post'; function _dataStringify(_data) { return Object.keys(_data).map(key => `${key}=${_data[key]}`).join('&') } // 拼接 data let requestData = null if (_isGet) { url += `?${_dataStringify(data)}` } else if (_isPost) { requestData = options.format === 'json' ? JSON.stringify(data) : _dataStringify(data) } return new Promise((resolve, reject) => { var xhr = new XMLHttpRequest(); xhr.withCredentials = Boolean(options.withCredentials); xhr.onload = function () { if (xhr.status == 200 || xhr.status == 304) { resolve(xhr.responseText); } else { reject('请求错误') } } // 绑定自定义 handler for (var ev in options.handlers) { var _prevHandler = xhr[ev]; xhr[ev] = function () { _prevHandler(); options.handlers[ev](); } } xhr.open(method, url, true); // 设置 header for (var attr in headers) { xhr.setRequestHeader(attr, headers[attr]); } xhr.send(requestData); })}// ajax 使用ajax({ url: 'http://localhost:9000', method: 'POST', format: 'json', data: { name: 'tom' }, headers: { // 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/json', 'x-token': 'xxxxx' }, withCredentials: true, handlers: { 'onload': function () { console.log('自定义onload') } }}) .then(res => { console.log(res,222222)}) .catch(err => { console.log(err)})
示例并不完整,可以把文件上传的 FormData 方式添加进去,还有超时等功能。
联系客服