我们知道 axios 的封装是基于 Promise 和 XMLHttpRequests,所以我们需要准备的前置知识有:
- ajax 的封装
- Promise
- json-server
Ajax的封装
ajax的封装在面试的过程中也很有可能会问到,而且很多时候,我们需要了解请求的原理,那么 ajax 总共分为 4 步骤:
1、创建异步对象
let xhr = null
// 异步对象需要对 IE6 做兼容处理
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest()
}else{
// IE6 的异步对象
xhr = new ActiveXObject('Mricosoft.XMLHTTP')
}2、链接服务器
get 和 post 的区别
1、get 如果需要传递参数需要在 url 地址栏后面拼接,而 post 不需要
2、post 需要加请求头,而 get 不需要
let url = '' // 请求的地址
let params = { // 发送给后端的参数
name:'张三'
}
// get 方式
xhr.open('get', `${url}?name=${params.name}`)
// post 方式
xhr.open('post',url)
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")3、发送请求
get 和 post 的区别
- get 在 send 里面传递 null
- post 在 send 里面传递对象
let params = {
name:'张三'
}
// get 方式
xhr.send(null)
// post 方式
xhr.send(params)4、接收请求的数据
xhr.onreadystatechange = function(){
// readyState:4 status:200 代表数据返回
if(xhr.readyState === 4 && xhr.status === 200){
// 拿到核心文本对象就是传递过来的数据
console.log(xhr.responseText)
}
}Ajax封装完整代码
let options = {
// 请求地址
url:'',
// 请求类型
type:'get/post',
// 传递的参数
data:{
name:'张三'
},
success:function(res){
console.log(res)
}
}
function ajax(options){
let xhr = null
// 1、创建异步对象
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest()
}else{
xhr = new ActiveXObject('Mricosoft.XMLHTTP')
}
// 2、链接服务器
if(options.type==='get'){
xhr.open(options.type,`${options.url}?name=${options.data.name}`)
xhr.send(null)
}else{
xhr.open(options.type,options.url)
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
xhr.send(options.data)
}
// 3、接收请求数据
xhr.onreadystatechange = function(){
if(xhr.readyState ===4 && xhr.status === 200){
options.success(xhr.responseText)
}
}
}Promise
Promise我们从几个问题来解释:
- Promise 的出现是为了解决什么问题 ?
- Promise 在解决问题的过程中暴露了什么问题,generator 如何解决它的问题 ?
- generator 暴露了什么问题,async 如何解决它的问题 ?
Promise 的出现为了解决什么问题 ?
Promise 的出现是为了解决回调地狱的问题的,但是它是如何解决回调地狱问题的呢,那么我们要知道 Promise 出现之前,我们用封装好的 ajax 去做异步请求,那么就存在一个问题,我们假设一个场景,我需求是请求三级联动的数据,省、市、区,分别三个接口,那我们会怎么操作,直接上代码,使用我们上面封装的 ajax
// 省、市、区
// 需求:通过选择省,再去发请求,选择城市,再发请求,请求到区
ajax({ // 全国有多少省
url:'',
type:'',
data:data,
success:function(res){
ajax({ // 当前省有多少市
url:'',
type:'',
data:data,
success:function(res){
ajax({ // 当前市有多少区
url:'',
type:'',
data:data,
success:function(res){
console.log(res) // 最终选择的区
}
})
}
})
}
})注意:这样就形成了我们所说的回调地狱,不好维护,层层嵌套回调
Promise是如何解决回调地狱的问题呢?(这里我就用 setTimeout 去模拟数据请求了)
// 先请求省的数据
new Promise(function(resolve,reject){
setTimeout(function(){
resolve('省')
},1000)
}).then(res=>{
console.log(res)
// 再请求市的数据
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve('市')
},1000)
})
}).then(res=>{
console.log(res)
// 再请求区的数据
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve('区')
},1000)
})
}).then(res=>{
console.log(res)
})注意:Promise,通过它独有的 then 的链式,将之前的三角回调地狱变成了,竖状结构,变得更好维护了。
问题:Promise 在解决回调地狱问题的时候也暴露了问题,就是需要返回一个 Promise 才能继续 then,那么接下来就是 generator 登场了
generator 的出现可以解决 Promise 带来的回调问题
怎么样去解决呢?
首先,什么是 generator 函数,它跟普通函数没有什么区别,只是在 function 后面,或者在函数名前面加 *
// 下面两个都属于 generator 函数
function* gen(){}
function *gen(){}其次,搭配 yield 使用
function *gen(){
yield new Promise()
}依然是省、市、区的例子
function* gen() {
let res1 = yield Promise.resolve('省')
let res2 = yield Promise.resolve('市')
let res3 = yield Promise.resolve('区')
}
// 需要用变量接收 generator 函数的执行
let g = gen()
// 需要使用 next 使用,当前只有省的数据,函数就停止执行console.log(g.next()) 
我们得到的结果是一个对象,其中 done 代表整个 generator 还没有执行完,如果所有的 yield 执行完了,这个值就是 true ,value 是一个 Promise 对象通过 then 可以拿到结果
将函数执行完,更改代码
function* gen() {
let res1 = yield Promise.resolve('省')
let res2 = yield Promise.resolve('市')
let res3 = yield Promise.resolve('区')
}
let g = gen()
g.next().value.then(res => {
console.log(res)
})
g.next().value.then(res => {
console.log(res)
})
g.next().value.then(res => {
console.log(res)
})结果:

多说一句,其实 generator 涉及到了协程的概念,如果你懂协程,说明你还是不错的。
注意:generator 虽然解决了 Promise 的回调的问题,但是它本身的问题也比较明显,就是需要 next ,所以 async 就登场了
async 解决了 generator 的 next 的问题
async 大家应该都很熟悉了,就不做过多的阐述,搭配 await 来使用
async function asyncFn() {
let res1 = await Promise.resolve('省')
console.log(res1)
let res2 = await Promise.resolve('市')
console.log(res2)
let res3 = await Promise.resolve('区')
console.log(res3)
}
let async = asyncFn()结果:

注意:很明显,async 不需要像 generator 那样 next , 直接一步到位,其实 async 就是 generator 的语法糖,async = generator + 自执行函数
json-server
这个工具可以直接像真实的请求后台数据一样,而且可以更改数据,并且很方便,后续都会用它来实现 axios 封装之后的调试
首先,安装
npm i -g json-server创建一个文件夹 server,在此文件夹中新建一个文件 db.json
// db.json 的内容
{ "data": [
{
"id": 1,
"name": "张三",
"age": "19"
},
{
"id": 2,
"name": "李四",
"age": "29"
}
]
}执行命令
json-server --watch --port 53000 db.json这个时候会开启服务

通过 http://localhost:53000/data 可以拿到数据,并且也可以修改 db.json 里的数据
我们使用 axios 进行用例测试
新建一个 html 文件,引入 axios
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button onclick="getFn()">get请求</button>
<button onclick="postFn()">post请求</button>
<button onclick="putFn()">put请求</button></body>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.js"></script>
<script>
function getFn() {
axios.get('http://localhost:53000/data/')
.then(res => {
console.log(res)
})
}
function postFn() {
axios.post('http://localhost:53000/data/',
{
id: 3,
name: "王二麻子"
}).then(res => {
console.log(res)
})
}
function putFn() {
axios.put('http://localhost:53000/data/2',
{
name: "赵四"
}).then(res => {
console.log(res)
}) }
</script>
</html>页面出来的样式

我们分别点击 get post put 看看会发生什么事情
get请求

post请求

post 之后 db.json 数据的变化

向数据里面追加了一条数据
put请求

put 之后 db.json 中数据变化

数据第二条 name 变更为 “赵四”
所以这个就是 json-server 的基本使用,后续还会用到。如果感兴趣可以去查看其他的 API,json-server
最后:本人喜欢研究面试题,希望有更多志同道合的朋友一起来交流研究,可以帮助修改简历
