Ajax看这一篇就够了!(原生+Axios)

本文详细介绍了Ajax的基本概念,包括URL和请求方式(GET、POST、PUT、PATCH、DELETE),如何使用axios库编写Ajax代码,以及请求体的不同格式如JSON和FormData。同时,文章还探讨了原生Ajax实现、Promise对象的使用,以及async和await修饰符在处理异步操作中的角色。最后,提到了错误处理、事件循环、宏任务和微任务的概念,以及如何根据接口文档编写Ajax代码。
摘要由CSDN通过智能技术生成

1. 请求-响应

2. URL和请求方式

它俩是一次请求,必须的条件。

发送请求的时候,必须 指定url和请求方式。

URL

  • ajax.itheima.net ---------- 作用是找服务器(不同的域名对应着不同的服务器)
  • /api/news ---------- 不要记,每个项目,地址都不同

请求方式:

  • GET --------- 获取数据
  • POST --------- 提交数据,一般都是为了新增数据
  • PUT --------- 修改数据(整体修改)
  • PATCH -------- 修改数据(局部修改)
  • DELETE -------- 删除数据
  • ...........

3. 如何写Ajax代码

  1. axios使用语法
  2. 接口文档
axios({
  url: '',
  method: '',
  params: {}, // 接口文档描述的是 query参数、查询参数,那么就写 params
  data: {},  // 接口文档描述的是 body参数、请求体,那么就写data
  // https://www.axios-http.cn/docs/req_config    全部的配置,都在这里
}).then(result => {
  result.data  表示服务器返回的数据
})

4. 请求体

请求体,不止一种格式,有很多格式。

常见的三种请求体格式:

请求体

示例

JSON格式

{ bookname: 'xxx' } --> axios会把对象转成JSON格式

非常常用

FormData对象

new FormData()

上传文件的唯一手段

查询字符串

'bookname=史记&author=司马迁&publisher=xxx'

5. FormData对象的用法

// 1. 实例化对象
let fd = new FormData()

// 2. 向对象中添加数据
fd.append('uname', 'laotang')
fd.append('age', 20)
fd.append('avatar', 文件对象) // 如何得到文件对象:  文件选择框.files[0]

// 3. AJAX提交数据
axios({
  method: '',
  url: '',
  data: fd
}).then(result => {
  
})

6. 原生的Ajax实现

// 1. 实例化对象 XMLHttpRequest 
let xhr = new XMLHttpRequest()

// 2. 注册事件,loadend事件,当服务器返回结果之后触发;response属性用于接收响应结果的
xhr.addEventListener('loadend', function () {
  // console.log(xhr.response) // 输出的就是服务器返回的结果,格式为JSON格式
  console.log(JSON.parse(xhr.response))
})

// 3. 调用open方法,设置请求方式、URL,如果有 query参数,携带query参数
// 3.1 没有query参数
xhr.open('请求方式', 'url')
// 3.2 有query参数
let p = { 参数: 值, 参数: 值 }
let u = new URLSearchParams(p)
xhr.open('请求方式', `url?${u.toString()}`)

// 4. 如果有body参数,也就是请求体,需要设置请求头
// 下面的这一行,表示提交的请求体是JSON格式的;如果提交FormData对象,这行不要写
xhr.setRequestHeader('Content-Type', 'application/json')

// 5. 调用send方法,发送请求;
xhr.send(请求体) // 请求体 有 JSON格式;FormData对象格式

7. 封装自己的axios函数

目的:

  • 锻炼编程能力
  • 了解真正的axios内部的实现
function myAxios ({ url, method = 'GET', params, data }) {
  if (!url) {
    throw new Error('url必填')
  }
  return new Promise((resolve, reject) => {
    // 1. 实例化 xhr 对象
    let xhr = new XMLHttpRequest()
    // 2. 注册 loadend 事件,并用 response接收响应结果
    xhr.addEventListener('loadend', function () {
      // xhr.status ---> 数字,响应状态码
      // console.log(xhr.status)
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.response))
      } else {
        reject(xhr.response)
      }
    })
    // 3. 调用open,设置请求方式、URL
    // console.log(params)
    if (params) {
      let u = new URLSearchParams(params)
      url = url + '?' + u.toString()
    }
    xhr.open(method, url)

    // 4. 调用send,发送请求
    if (data) {
      xhr.setRequestHeader('Content-Type', 'application/json')
      xhr.send(JSON.stringify(data))
    } else {
      xhr.send()
    }
  })
}

调用自己封装的函数:

// 测试3:发送请求,带body参数(请求体)
myAxios({
  method: 'POST',
  url: 'http://hjax.it.net/api/register', // 必填
  data: {
    username: 'laotang108',
    password: '123123'
  }
}).then(result => {
  console.log('这里是then')
  console.log(result)
}).catch(err => {
  console.log(err)
})

8. Promise对象

基本语法格式

Promise是“承诺”的意思,实例中,它里面的异步操作就相当于一个承诺,而承诺就会有两种结果,要么完成了承诺的内容,要么失败。

所以,使用Promise,分为两大部分,首先是有一个承诺(异步操作),然后再兑现结果。

第一部分:定义“承诺”

// 实例化一个Promise,需要给它传递一个函数作为参数,而该函数又有两个形参,通常用resolve和reject来表示。该函数里面可以写异步请求的代码
// 换个角度,也可以理解为定下了一个承诺
let p = new Promise((resolve, reject) => {
  // 形参resolve,单词意思是 完成
  // 形参reject ,单词意思是 失败
  // ”承诺” 一般都需要一定的期限才能完成,当然也可能失败
  // 比如,我承诺 3 年后赚 500万,然后回来娶你。
  // 上述承诺可能成功的完成,也可能失败
  // 在编程中,如果承诺成功的完成,则调用 resolve 函数;失败调用 reject 函数
  setTimeout(() => {
    let money = Math.floor(Math.random() * 1000)
    if (money >= 500) {
      resolve('赚到钱了,回来娶你')
    } else {
      reject('赚钱太难,找个好人嫁了吧')
    }
  }, 3000)
});

第二部分:获取“承诺”的结果:

// 通过调用 Promise对象 的then方法和catch方法,分别获取成功的结果和失败的结果
p.then(res => {
  console.log(res) // 赚到钱了,回来娶你
}).catch(err => {
  console.log(err) // 赚钱太难,找个好人嫁了吧
})

9. async 和 await 修饰符

async和await是获取 Promise对象的结果的另一个方案,用起来比then更简单。

基本语法如下:

// 1. 声明一个 async 函数
async function abc () {
  let 结果 = await Promise对象
}

abc()
  • async 只能放到函数前(函数可以是任意的函数)
  • async 要放到距离await最近的那个函数前面
  • await 只能出现在 async 函数中。
  • await 会暂停函数的执行,await下一行代码相当于是微任务。

10. Promise.all()静态方法

语法格式如下:

Promise.all([ Promise对象, Promise对象, Promise对象 ]).then(res => {
  
})

all是所有的意思,等所有的Promise对象都完成,会触发then,res包含所有的3个结果。

其他静态方法:

Promise.resolve() -- 得到成功状态的promise对象

Promise.reject() -- 得到失败状态的promise对象

Promise.race() -- 和all的语法一样,res 只是最快的那个Promise对象的结果

如何得到Promise对象:

  • 自己 new Promise(),将成功的结果传给resolve,将失败的结果传递给reject
  • 调用 Promise.all() 等静态方法
  • 调用 axios() 可以得到 Promise 对象

如何获取Promise对象的结果:

  • 对象.then().catch()
  • async 和 await 配置,可以得到结果

11. 错误处理

如果你用 .then 获取结果,则在尾端加一个 .catch 即可收集错误信息。

如果你用 async和await获取结果,则用 try...catch...语句收集错误信息。

// 下面的请求地址,故意写错,调试用的
let p = axios({ url: 'http://ajax.it.net/api/province123' })

  // 获取结果,方案一:使用 then
  // p.then(result => {
  //   console.log(result)
  // }).catch(err => {
  //   console.log('这里是catch')
  //   console.log(err)
  // })

  // 获取结果,方案二:使用 async和await
  ; (async function () {
    try {
      // 把全部的代码,都放到try里面
      let result = await p
      console.log(result)
    } catch (e) {
      console.log('进入了catch')
      console.log(e)
    }
  })()

12. 了解then的链式调用

对象.then().then().then().catch() 像这样连续的调用then、catch,就是链式调用。

// 1. 得到Promise对象
let p1 = axios({ url: 'http://ajax.itheima.net/api/province' })
let p2 = axios({ url: 'http://ajax.itheima.net/api/news' })
let p3 = axios({ url: 'http://ajax.itheima.net/api/category/top' })

// 2. 获取结果
p1.then(result => {
  console.log(result.data) // 第1个请求的结果
  return p2
}).then(result => {
  console.log(result.data) // 第2个请求的结果
  return p3
}).then(result => {
  console.log(result.data) // 第3个请求的结果
}).catch(err => {
  console.log(err)
})

13. 宏任务和微任务、事件循环

JavaScript是单线程的,也就是说,同一个时刻,JavaScript只能执行一个任务,其他任务只能等待。

14. JavaScript是单线程的

js是运行于浏览器的脚本语言,因其经常涉及操作dom,如果是多线程的,也就意味着,同一个时刻,能够执行多个任务。

试想,如果一个线程修改dom,另一个线程删除dom,那么浏览器就不知道该先执行哪个操作。

所以js执行的时候会按照一个任务一个任务来执行。

最重要的原因,是JS在设计的年代,计算机的配置非常低下,根本没有考虑多线程的事。

15. 同步任务和异步任务

试想一下,如果js的任务都是同步的,那么遇到定时器、网络请求等这类型需要延时执行的任务会发生什么?

页面可能会瘫痪,需要暂停下来等待这些需要很长时间才能执行完毕的代码

所以,又引入了异步任务。

  • 同步任务:同步任务不需要进行等待可立即看到执行结果,比如console
  • 异步任务:异步任务需要等待一定的时候才能看到结果,比如 setTimeout、网络请求、DOM事件

16. 事件循环(Event Loop)

事件循环的比较简单,它是一个在 "JavaScript 引擎等待任务","执行任务"和"进入休眠状态等待更多任务"这几个状态之间转换的无限循环。

引擎的一般算法:

  1. 当有任务时:
  • 从最先进入的任务开始执行。
  • 没有其他任务,休眠直到出现任务,然后转到第 1 步。

17. 任务队列

根据规范,事件循环是通过任务队列的机制来进行协调的。一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合;每个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。setTimeout/Promise 等API便是任务源。

在事件循环中,每进行一次循环的关键步骤如下:

  • 在此次循环中选择最先进入队列的任务(oldest task),如果有则执行(一次)
  • 检查是否存在 微任务(Microtasks),如果存在则不停地执行,直至清空 微任务队列(Microtasks Queue)
  • 更新 render(DOM渲染)
  • 以上为一次循环,主线程重复执行上述步骤

在上述循环的基础上需要了解几点:

  • JS分为同步任务和异步任务
  • 同步任务都在主线程上执行,形成一个执行栈
  • 主线程之外,宿主环境管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
  • 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。

18. 宏任务

(macro)task,可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。

任务(代码)

宏任务

环境

script

宏任务

浏览器

事件

宏任务

浏览器

网络请求(Ajax)

宏任务

浏览器

setTimeout() 定时器

宏任务

浏览器 / Node

fs.readFile() 读取文件

宏任务

Node

比如去银行排队办业务,每个人的业务就相当于是一个宏任务;

19. 微任务

微任务(microtask)是宏任务中的一个部分,它的执行时机是在同步代码执行之后,下一个宏任务执行之前。

微任务包含

Promise.then
await

比如一个人,去银行存钱,存钱之后,又进行了一些了操作,比如买纪念币、买理财产品、办信用卡,这些就叫做微任务。

20. 运行机制

在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:

  • 执行一个宏任务(执行栈中没有就从事件队列中获取)
  • 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
  • 宏任务里的同步代码执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

21. 面试题分析

console.log(1)
new Promise((resolve, reject) => {
  console.log(2)
  resolve(3)
  console.log(4)
}).then(res => {
  console.log(res) // 3
})

setTimeout(() => {
  console.log(5)
}, 100)

async function abc() {
  await console.log(6) // 左右结合的代码,右先执行
  console.log(7)
}
abc()
console.log(8)
setTimeout(() => {
  console.log(9)
}, 10)
console.log(10)

22.看接口文档写代码

第一种情况

接口文档描述:Query参数 或 查询参数

具体到代码:

axios({
  method: 'XXX',
  url: 'xxxxxxxxxxxxxxxxxxxxx',
  params: {
    参数: 值, 
    参数: 值
  }
})

第二种情况

接口文档描述:/api/getbooks/:id/:appkey

具体到代码:

:xxx 换成具体的值即可。

axios({
  method: 'XXX',
  url: '/api/getbooks/100/laotang', // 这里的100就是id值,laotang就是appkey的值
})

第三种情况(少)

接口文档描述:

  • 请求体 或 body参数
  • 并指定 Content-Type: application/x-www-form-urlencoded

具体到代码:

axios({
  method: 'XXXX',
  url: 'xxxxxxxxxxxxxxxxxxx',
  data: '参数=值&参数=值'  // data要写查询字符串格式
})

第四种情况(多)

接口文档描述:

  • 请求体 或 body参数
  • 并指定 Content-Type: application/json

具体到代码:

axios({
  method: 'XXXX',
  url: 'xxxxxxxxxxxxxxxxxxx',
  data: {  // data写对象格式,axios内部会自动将对象转成JSON
    参数: 值,
    参数: 值
  }
})

第五种情况

接口文档描述:

  • 请求体 或 body参数
  • 并指定 Content-Type: multipart/form-data

具体到代码:

axios({
  method: 'XXXX',
  url: 'xxxxxxxxxxxxxxxxxxx',
  data: fd // fd 就是 new FormData() 得来的对象
})

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr Web

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值