AJAX

  • 使用浏览器的 XMLHttpRequest 对象 与服务器通信,动态数据交互

  • 浏览器网页中,使用 AJAX技术(XHR对象)发起获取数据的请求,服务器代码响应准备好的数据给前端,前端拿到数据数组以后,展示到网页

  • 学习AJAX这里使用 第三方库axios,axios库语法简单

axios使用

  1. 引入 axios.js

    axios.js文件链接: https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js

    学习vue这些框架后可以用npm包管理器来安装axios

  2. axios函数的使用语法

    axios({
      url: '目标资源地址'
    }).then((result) => {
      // 对服务器返回的数据做后续处理
    })

    注意:请求的 url 地址, 就是标记资源的网址

URL

统一资源定位符,简称网址,用于定位网络中的资源(资源指的是:网页,图片,数据,视频,音频等等)

组成:协议,域名,资源路径(URL 组成有很多部分,我们先掌握这3个重要的部分即可)

  • 协议:超文本传输协议,规定了浏览器和服务器传递数据的格式

  • 域名:标记服务器在互联网当中的方位,网络中有很多服务器,你想访问哪一台,就需要知道它的域名才可以

  • 资源路径:一个服务器内有多个资源,用于标识你要访问的资源具体的位置

url查询参数

携带给服务器额外信息,让服务器返回我想要的某一部分数据而不是全部数据

在 url 网址后面用?拼接格式:http://xxxx.com/xxx/xxx?参数名1=值1&参数名2=值2

参数名一般是后端规定的,值前端看情况传递即可

常用请求方法和数据提交

请求方法:GET,POST,PUT,DELETE,PATCH(这些都是http协议规定的),每个单词对应一种对服务器资源要执行的操作

常用的 get 和 post

// get请求 获取数据 默认 可不写method,传参params
axios({
  url: '目标资源地址',
  params: {
    参数名: 值
  }
}).then(result => {
  // 对服务器返回的数据做后续处理
})
​
​
// post请求 提交数据 method里写请求方法,data写参数
axios({
  url: '目标资源地址',
  method: 'POST',
  data: {
    参数名: 值
  }
}).then(result => {
  // 对服务器返回的数据做后续处理
})
​
// 错误处理 catch捕获这次请求响应的错误并做后续处理
axios({
  // ...请求选项
}).then(result => {
  // 处理成功数据
}).catch(error => {
  // 处理失败错误
  console.log(error)
  console.log(error.response.data.message)
  alert(error.response.data.message)
})

HTTP 协议-请求报文

HTTP 协议规定了浏览器和服务器返回内容的格式

请求报文:是浏览器按照协议规定发送给服务器的内容

这里的格式包含:

  • 请求行:请求方法,URL,协议

  • 请求头:以键值对的格式携带的附加信息,比如:Content-Type(指定了本次传递的内容类型)

  • 空行:分割请求头,空行之后的是发送给服务器的资源

  • 请求体:发送的资源

HTTP 协议-响应报文

响应报文:是服务器按照协议固定的格式,返回给浏览器的内容

响应报文的组成:

  • 响应行(状态行):协议,HTTP响应状态码,状态信息

  • 响应头:以键值对的格式携带的附加信息,比如:Content-Type(告诉浏览器,本次返回的内容类型)

  • 空行:分割响应头,控制之后的是服务器返回的资源

  • 响应体:返回的资源

HTTP 响应状态码:

  • 用来表明请求是否成功完成

  • 例如:404(客户端要找的资源,在服务器上不存在)

XMLHttpRequest - 基础使用

axios 是对 XHR 相关代码进行了封装,让我们只关心传递的接口参数

XHR语法如下:

// 1. 创建 XMLHttpRequest 对象
const xhr = new XMLHttpRequest()
// 2. 配置请求方法和请求 url 地址
xhr.open('请求方法', '请求url网址')
// 3. 监听 loadend 事件,接收响应结果
xhr.addEventListener('loadend', () => {
  // 响应结果
  console.log(xhr.response)
})
// 4. 发起请求
xhr.send()

XMLHttpRequest - 查询参数

原生 XHR 需要自己在 url 后面携带查询参数字符串,没有 axios 帮助我们把 params 参数拼接到 url 字符串后面了

但是多个查询参数,如果自己拼接很麻烦,这里用 URLSearchParams把参数对象转成“参数名=值&参数名=值“格式的字符串,语法如下:

// 1. 创建 URLSearchParams 对象
const paramsObj = new URLSearchParams({
  参数名1: 值1,
  参数名2: 值2
})
​
// 2. 生成指定格式查询参数字符串
const queryString = paramsObj.toString()
// 结果:参数名1=值1&参数名2=值2

XMLHttpRequest- 数据提交

  • 需要自己设置请求头 Content-Type:application/json,来告诉服务器端,我们发过去的内容类型是 JSON 字符串,让他转成对应数据结构取值使用

  • 需要前端自己把 JS 对象转成 JSON 字符串

  • 原生 XHR 需要在 send 方法调用时,传入请求体携带

核心代码如下:

/**
 * 目标:使用xhr进行数据提交-完成注册功能
*/
document.querySelector('.reg-btn').addEventListener('click', () => {
  const xhr = new XMLHttpRequest()
  xhr.open('POST', 'http://hmajax.itheima.net/api/register')
  xhr.addEventListener('loadend', () => {
    console.log(xhr.response)
  })
​
  // 设置请求头-告诉服务器内容类型(JSON字符串)
  xhr.setRequestHeader('Content-Type', 'application/json')
  // 准备提交的数据
  const userObj = {
    username: 'itheima007',
    password: '7654321'
  }
  const userStr = JSON.stringify(userObj)
  // 设置请求体,发起请求
  xhr.send(userStr)
})

Promise

Promise 对象用于表示一个异步操作的最终完成(或失败)及其结构值

Promise 管理异步任务,语法怎么用?

// 1. 创建 Promise 对象
const p = new Promise((resolve, reject) => {
 // 2. 执行异步任务-并传递结果
 // 成功调用: resolve(值) 触发 then() 执行
 // 失败调用: reject(值) 触发 catch() 执行
})
// 3. 接收结果
p.then(result => {
 // 成功
}).catch(error => {
 // 失败
})

Promise 的状态

每个 Promise 对象必定处于以下三种状态之一

  1. 待定(pending):初始状态,既没有被兑现,也没有被拒绝

  2. 已兑现(fulfilled):操作成功完成

  3. 已拒绝(rejected):操作失败

每个 Promise 对象一旦被兑现/拒绝,那就是已敲定了,状态无法再被改变

/**
 * 目标:使用Promise管理XHR请求省份列表
 *  1. 创建Promise对象
 *  2. 执行XHR异步代码,获取省份列表
 *  3. 关联成功或失败函数,做后续处理
*/
// 1. 创建Promise对象
const p = new Promise((resolve, reject) => {
  // 2. 执行XHR异步代码,获取省份列表
  const xhr = new XMLHttpRequest()
  xhr.open('GET', 'http://hmajax.itheima.net/api/province')
  xhr.addEventListener('loadend', () => {
    // xhr如何判断响应成功还是失败的?
    // 2xx开头的都是成功响应状态码
    if (xhr.status >= 200 && xhr.status < 300) {
      resolve(JSON.parse(xhr.response))
    } else {
      reject(new Error(xhr.response))
    }
  })
  xhr.send()
})
​
// 3. 关联成功或失败函数,做后续处理
p.then(result => {
  console.log(result)
  document.querySelector('.my-p').innerHTML = result.list.join('<br>')
}).catch(error => {
  // 错误对象要用console.dir详细打印
  console.dir(error)
  // 服务器返回错误提示消息,插入到p标签显示
  document.querySelector('.my-p').innerHTML = error.message
})

封装_简易axios

基于 Promise 和 XHR 封装 myAxios 函数

核心代码:

// 定义myAxios函数,接收配置对象,返回Promise对象
function myAxios(config) {
  return new Promise((resolve, reject) => {
    // 发起XHR请求,默认请求方法为GET
    const xhr = new XMLHttpRequest()
    // 判断有params选项,携带查询参数
    if (config.params) {
      // 使用URLSearchParams转换,并携带到url上
      const paramsObj = new URLSearchParams(config.params)
      const queryString = paramsObj.toString()
      // 把查询参数字符串,拼接在url?后面
      config.url += `?${queryString}`
    }
    xhr.open(config.method || 'GET', config.url)
​
    xhr.addEventListener('loadend', () => {
      // 调用成功/失败的处理程序
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(JSON.parse(xhr.response))
      } else {
        reject(new Error(xhr.response))
      }
    })
    // 判断有data选项,携带请求体
    if (config.data) {
      // 转换数据类型,在send中发送
      const jsonStr = JSON.stringify(config.data)
      xhr.setRequestHeader('Content-Type', 'application/json')
      xhr.send(jsonStr)
    } else {
      // 如果没有请求体数据,正常的发起请求
      xhr.send()
    }
  })
}
​
// 使用
document.querySelector('.reg-btn').addEventListener('click', () => {
  // 使用myAxios函数,完成注册用户
  myAxios({
    url: 'http://hmajax.itheima.net/api/register',
    method: 'POST',
    data: {
      username: 'itheima999',
      password: '666666'
    }
  }).then(result => {
    console.log(result)
  }).catch(error => {
    console.dir(error)
  })
})

同步代码和异步代码

  1. 同步代码:逐行执行,需原地等待结果后,才继续向下执行

  1. 异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发回调函数传递结果

回调函数地狱

在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱;可读性差,异常无法捕获,耦合性严重,牵一发动全身

axios({ url: 'http://hmajax.itheima.net/api/province' }).then(result => {
  const pname = result.data.list[0]
  document.querySelector('.province').innerHTML = pname
  // 获取第一个省份默认下属的第一个城市名字
  axios({ url: 'http://hmajax.itheima.net/api/city', params: { pname } }).then(result => {
    const cname = result.data.list[0]
    document.querySelector('.city').innerHTML = cname
    // 获取第一个城市默认下属第一个地区名字
    axios({ url: 'http://hmajax.itheima.net/api/area', params: { pname, cname } }).then(result => {
      document.querySelector('.area').innerHTML = result.data.list[0]
    })
  })
})

Promise-链式调用

  1. 概念:依靠 then() 方法会返回一个新生成的 Promise 对象特性,继续串联下一环任务,直到结束

  2. 细节:then() 回调函数中的返回值,会影响新生成的 Promise 对象最终状态和结果

  3. 好处:通过链式调用,解决回调函数嵌套问题

/**
 * 目标:掌握Promise的链式调用
 * 需求:把省市的嵌套结构,改成链式调用的线性结构
*/
// 1. 创建Promise对象-模拟请求省份名字
const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('北京市')
  }, 2000)
})

// 2. 获取省份名字
const p2 = p.then(result => {
  console.log(result)
  // 3. 创建Promise对象-模拟请求城市名字
  // return Promise对象最终状态和结果,影响到新的Promise对象
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(result + '--- 北京')
    }, 2000)
  })
})

// 4. 获取城市名字
p2.then(result => {
  console.log(result)
})

// then()原地的结果是一个新的Promise对象
console.log(p2 === p)
  1. 目标:使用 Promise 链式调用,解决回调函数地狱问题

  2. 做法:每个 Promise 对象中管理一个异步任务,用 then 返回 Promise 对象,串联起来

  3. 按照图解思路,编写核心代码:

    /**
     * 目标:把回调函数嵌套代码,改成Promise链式调用结构
     * 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
    */
    let pname = ''
    // 1. 得到-获取省份Promise对象
    axios({url: 'http://hmajax.itheima.net/api/province'}).then(result => {
      pname = result.data.list[0]
      document.querySelector('.province').innerHTML = pname
      // 2. 得到-获取城市Promise对象
      return axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})
    }).then(result => {
      const cname = result.data.list[0]
      document.querySelector('.city').innerHTML = cname
      // 3. 得到-获取地区Promise对象
      return axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
    }).then(result => {
      console.log(result)
      const areaName = result.data.list[0]
      document.querySelector('.area').innerHTML = areaName
    })

async 和 await 函数

  1. 概念:在 async 函数内,使用 await 关键字取代 then 函数,等待获取 Promise 对象成功状态的结果值

  2. 做法:使用 async 和 await 解决回调地狱问题

  3. 核心代码:

    /**
     * 目标:掌握async和await语法,解决回调函数地狱
     * 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
     * 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
    */
    // 1. 定义async修饰函数
    async function getData() {
      // 2. await等待Promise对象成功的结果
      const pObj = await axios({url: 'http://hmajax.itheima.net/api/province'})
      const pname = pObj.data.list[0]
      const cObj = await axios({url: 'http://hmajax.itheima.net/api/city', params: { pname }})
      const cname = cObj.data.list[0]
      const aObj = await axios({url: 'http://hmajax.itheima.net/api/area', params: { pname, cname }})
      const areaName = aObj.data.list[0]
    
    
      document.querySelector('.province').innerHTML = pname
      document.querySelector('.city').innerHTML = cname
      document.querySelector('.area').innerHTML = areaName
    }
    
    getData()

    使用 await 替代 then 的方法

事件循环

  1. 事件循环(EventLoop):掌握后知道 JS 是如何安排和运行代码的

  2. 作用:事件循环负责执行代码,收集和处理事件以及执行队列中的子任务

  3. 原因:JavaScript 单线程(某一刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,设计了事件循环模型

  4. 概念:执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里回调函数的执行机制,就叫事件循环

宏任务与微任务

  1. ES6 之后引入了 Promise 对象, 让 JS 引擎也可以发起异步任务

  2. 异步任务划分为了

    • 宏任务:由浏览器环境执行的异步代码

    • 微任务:由 JS 引擎环境执行的异步代码

  3. 宏任务和微任务具体划分:

    1. ES6 之后引入了 Promise 对象, 让 JS 引擎也可以发起异步任务

    2. 异步任务划分为了

      • 宏任务:由浏览器环境执行的异步代码

      • 微任务:由 JS 引擎环境执行的异步代码

    3. 宏任务和微任务具体划分:

注意:宏任务每次在执行同步代码时,产生微任务队列,清空微任务队列任务后,微任务队列空间释放!

下一次宏任务执行时,遇到微任务代码,才会再次申请微任务队列空间放入回调函数消息排队

总结:一个宏任务包含微任务队列,他们之间是包含关系,不是并列关系

Promise.all静态方法

概念:合并多个 Promise 对象,等待所有同时成功完成(或某一个失败),做后续逻辑

const p = Promise.all([Promise对象, Promise对象, ...])
p.then(result => {
  // result 结果: [Promise对象成功结果, Promise对象成功结果, ...]
}).catch(error => {
  // 第一个失败的 Promise 对象,抛出的异常对象
})
/**
     * 目标:掌握Promise的all方法作用,和使用场景
     * 业务:当我需要同一时间显示多个请求的结果时,就要把多请求合并
     * 例如:默认显示"北京", "上海", "广州", "深圳"的天气在首页查看
     * code:
     * 北京-110100
     * 上海-310100
     * 广州-440100
     * 深圳-440300
    */
    // 1. 请求城市天气,得到Promise对象
    const bjPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '110100' } })
    const shPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '310100' } })
    const gzPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440100' } })
    const szPromise = axios({ url: 'http://hmajax.itheima.net/api/weather', params: { city: '440300' } })

    // 2. 使用Promise.all,合并多个Promise对象
    const p = Promise.all([bjPromise, shPromise, gzPromise, szPromise])
    p.then(result => {
      // 注意:结果数组顺序和合并时顺序是一致
      console.log(result)
      const htmlStr = result.map(item => {
        return `<li>${item.data.data.area} --- ${item.data.data.weather}</li>`
      }).join('')
      document.querySelector('.my-ul').innerHTML = htmlStr
    }).catch(error => {
      console.dir(error)
    })

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐