实现原生Ajax
简介
AJAX = 异步 JavaScript 和 XML。
AJAX 是一种用于创建快速动态网页的技术。
通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
详细介绍请移步AJAX 简介 | 菜鸟教程
最简版本
function myAjax(method, url) {
const xhr = new XMLHttpRequest()
xhr.open(method, url)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 304) {
console.log(xhr.responseText);
} else {
console.log(new Error(xhr.statusText));
}
}
}
xhr.send()
}
带promis版本
function myAjax(method, url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open(method, url)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 304) {
resolve(xhr.responseText)
} else {
resolve(new Error(xhr.statusText))
}
}
}
xhr.send()
})
}
实现Axios
在平时写项目中大部分都会用到axios,但是对axios的实现却没怎么了解过,今天趁着休息,用js实现axios
简介
Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。
具体详细介绍请移步起步 | Axios中文文档 | Axios中文网
这里实现出几个主要的方法,包括post请求方法,create配置方法,以及拦截器的方法。
1.post方法
实现前先写一个类,在类的原型对象上进行方法的实现
function cAxios() {
}
实现时注意两点:
- axios的post的方法返回的是一个promise对象
- axios的post的方法的data参数是一个对象,而原生的XMLHttpRequest对象的send方法参数是string
所以需要在send方法里面把data参数转化为JSON格式的字符串
cAxios.prototype.post = function (method, url, data) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open(method, url)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 304) {
resolve(xhr.responseText)
} else {
reject(new Error(reject.statusText))
}
}
}
xhr.send(JSON.stringify(data))
})
}
get方法也是一样的,不再描述
2.create方法
axios.create(config)
- 根据指定配置创建一个新的 axios, 也就就每个新 axios 都有自己的配置(主要是请求头的配置)
- 新 axios 只是没有取消请求和批量发请求的方法, 其它所有语法都是一致的
这里的实现只是列举了headers和baseUrl这两个属性
cAxios.prototype.create = function (obj) {
let axios = new cAxios()
axios.headers = obj.headers;
axios.baseUrl = obj.baseUrl;
return axios
}
所以如果我们设置请求头以后,调用post方法需要带上这些参数,因此post方法需要修改
首先需要写一个方法来配置请求头
function setHeaders(xhr, headers){
for(let key in headers) {
xhr.setquestHeader(key, headers[key])
}
}
在post方法中,在ajax发送请求之前调用即可
setHeaders(xhr, this.headers)
3.拦截器
axios的拦截器分为请求拦截器和响应拦截器
含义:在请求或响应被 then 或 catch 处理前拦截它们。
官网实例
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error);
});
这里可以看到interceptors是一个对象,里面包含request和response属性
本文实现不考虑use
我们需要在类里面实现一下interceptors
this.interceptors = {
request(fn) {
_this.saveReqFn.push(fn)
},
response(fn) {
_this.saveResFn.push(fn)
}
}
如果我们想在外边调用请求拦截器或者响应拦截器,需要传递一个回调函数作为拦截器的参数
所以在我们这里我们需要把这个回调函数保存下来(考虑到有多个拦截器,所以我们采用数组保存)。
同时我们也要写一个data用于存放请求时的数据(因为在请求拦截器的回调函数中,有一个config参数,它包含着请求的数据)
function cAxios() {
// 保存请求拦截器中的回调函数
this.saveReqFn = []
// 保存响应拦截器中的回调函数
this.saveResFn = []
// 保存请求的数据
this.data = []
let _this = this
this.interceptors = {
request(fn) {
_this.saveReqFn.push(fn)
},
response(fn) {
_this.saveResFn.push(fn)
}
}
}
如何调用?
比如在post方法中,请求拦截器应该发送请求之前调用,响应拦截器在返回数据之前调用
cAxios.prototype.post = function (method, url, data) {
// 保存请求的数据
this.data = data
let _this = this
// 在发送请求之前调用请求拦截器的回调函数
if (this.saveReqFn?.length) {
this.saveReqFn.forEach(fn => {
fn(this)
})
}
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open(method, url)
setHeaders(xhr, this.headers)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 304) {
// 创建一个新的对象,将响应的数据保存到新的对象中
let newData = {}
newData.data = JSON.parse(xhr.responseText)
// 在返回数据之前调用响应拦截器的回调函数
if (_this.saveResFn?.length) {
_this.saveResFn.forEach(fn => {
fn(newData)
})
}
resolve(xhr.responseText)
} else {
reject(new Error(xhr.statusText))
}
}
}
xhr.send(JSON.stringify(data))
})
}
4.完整代码
function cAxios() {
// 保存请求拦截器中的回调函数
this.saveReqFn = []
// 保存响应拦截器中的回调函数
this.saveResFn = []
// 保存请求的数据
this.data = []
let _this = this
this.interceptors = {
request(fn) {
_this.saveReqFn.push(fn)
},
response(fn) {
_this.saveResFn.push(fn)
}
}
}
function setHeaders(xhr, headers) {
for (let key in headers) {
xhr.setquestHeader(key, headers[key])
}
}
cAxios.prototype.create = function (obj) {
let axios = new cAxios()
axios.headers = obj.headers;
axios.baseUrl = obj.baseUrl;
return axios
}
cAxios.prototype.post = function (method, url, data) {
// 保存请求的数据
this.data = data
let _this = this
// 在发送请求之前调用请求拦截器的回调函数
if (this.saveReqFn?.length) {
this.saveReqFn.forEach(fn => {
fn(this)
})
}
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open(method, url)
setHeaders(xhr, this.headers)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 304) {
// 创建一个新的对象,将请求的数据保存到新的对象中
let newData = {}
newData.data = JSON.parse(xhr.responseText)
// 在返回数据之前调用响应拦截器的回调函数
if (_this.saveResFn?.length) {
_this.saveResFn.forEach(fn => {
fn(newData)
})
}
resolve(xhr.responseText)
} else {
reject(new Error(xhr.statusText))
}
}
}
xhr.send(JSON.stringify(data))
})
}
let axios = new cAxios()
export {
axios
}