【AJAX】XMLHttpRequest、Promise 与 axios的关系

发布于:2025-07-25 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

一、AJAX原理 —— XMLHttpRequest

1.1 使用XMLHttpRequest

二、 XMLHttpRequest - 查询参数 (就是往服务器后面拼接要查询的字符串)

三、 地区查询

四、 XMLHttpRequest - 数据提交

五、 认识Promise

5.1 为什么 JavaScript 需要异步?

5.2 Promiss - 三种状态

5.3 使用Promiss + XHM 获取省份列表

六、 封装简易的axios——获取省份列表

七、 注册账号——支持传递请求体数据 data选项

总结不易~ 本章节对我有很大的收获,希望对你也是!!!


本节素材已上传至Gitee:https://gitee.com/liu-yihao-hhh/ajax_studyhttps://gitee.com/liu-yihao-hhh/ajax_study

一、AJAX原理 —— XMLHttpRequest

定义

XMLHttpRequest(XHR) 对象用于与服务器交互。通过XMLHttpRequst可以再不刷新页面的情况下请求特定URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest在AJAX编程中被大量使用。

与axios的关系: axios内部采用XMLHttpRequest 与服务器交互

1.1 使用XMLHttpRequest

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

    // 发送 - 请求
    xhr.send()

XMLHttpRequest基础使用,获取服务器的数据并展示

    // 1. 创建一个XMR对象
    const xhr = new XMLHttpRequest()

    // 2. 配置请求方法和请求url地址
    xhr.open('GET', 'http://hmajax.itheima.net/api/province')

    // 3. 监听 loadend事件 接收响应结果
    xhr.addEventListener('loadend', () => {
      console.log(xhr.response) // 这里返回的是json字符串

      // json字符串转对象
      const data = JSON.parse(xhr.response)
      console.log(data.list.join('<br>')) // 数组转字符串进行拼接
      document.querySelector('.my-p').innerHTML = data.list.join('<br>')

    })

    // 4. 发起请求
    xhr.send()

二、 XMLHttpRequest - 查询参数 (就是往服务器后面拼接要查询的字符串)

定义: 浏览器提供给服务器的额外信息, 让服务器返回浏览器想要的数据

    /**
     * 目标:使用XHR携带查询参数,展示某个省下属的城市列表
    */

    const xhr = new XMLHttpRequest()
    // 进行查询 往服务器后面进行拼接查询的参数名 ?pname=
    xhr.open('GET', 'http://hmajax.itheima.net/api/city?pname=辽宁省')

    // loadend 加载结束事件
    xhr.addEventListener('loadend', () => {
      console.log(xhr.response)
      const data = JSON.parse(xhr.response)
      console.log(data.list)
      document.querySelector('.city-p').innerHTML = data.list.join('<br>')
    })

    xhr.send()

三、 地区查询

这一个案例就是要我们同时传入两个参数,但是我们不方便自己获取两个参数后拼接到url后面,这个时候,我们就可以采用浏览器内置的构造函数URLSearchParams 来创建一个对象,里面放入我们需要传入URL的对象参数,他就会自动给我们返回一个url编码的字符串,我们就可以直接在url后面进行拼接  ?+编码即可

    /**
     * 目标: 根据省份和城市名字, 查询对应的地区列表
    */

    // 1. 给查询按钮绑定一个点击事件
    document.querySelector('.sel-btn').addEventListener('click', () => {
      // 2. 收集省份和城市名字
      const pname = document.querySelector('.province').value
      const cname = document.querySelector('.city').value

      // 3. 组织查询参数字符串
      const qObj = {
        pname,
        cname
      }

      // 查询参数对象 -》 查询参数字符串
      // 浏览器内置的构造函数
      // 1. 创建 URLSearchParams 对象 自动将我们需要的多个查询对象转换成一个字符串 好方便后续的拼接
      const paramsObj = new URLSearchParams(qObj)
      const queryString = paramsObj.toString()
      console.log(queryString) // 拿到一个url编码 pname=%E6%B9%96%E5%8C%97&cname=%E6%AD%A6%E6%B1%89%E5%B8%82 

      // 4. 使用XHR对象, 查询地区列表
      const xhr = new XMLHttpRequest()
      xhr.open('GET', `http://hmajax.itheima.net/api/area?${queryString}`)

      // loadend 加载结束事件
      xhr.addEventListener('loadend', () => {
        console.log(xhr.response)
        const data = JSON.parse(xhr.response)
        console.log(data)
        const htmlStr = data.list.map(item => {
          return `<li class="list-group-item">${item}</li>`
        })
        console.log(htmlStr)
        const html = htmlStr.join('<br>')
        document.querySelector('.list-group').innerHTML = html
      })
      xhr.send()
    })

四、 XMLHttpRequest - 数据提交

需求: 通过XHR提交用户名和密码,完成注册功能

核心: 请求头设置Content-Type:application/json

请求体携带JSON字符串

 提交数据就是在xhr.send()中进行提交,但是要提前设置请求头,告诉服务器我们提交的数据是JSON数据

      xhr.setRequestHeader('Content-Type', 'application/json')

 准备好要进行提交的数据之后,就将该数据转换成JSON字符串直接进行提交即可!

    /**
     * 目标:使用xhr进行数据提交-完成注册功能
    */

    // 后端的数据提交 http://hmajax.itheima.net/api/register
    // 请求参数 body application/json
    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: '我是hhhhha',
        password: '11111111'
      }
      const userStr = JSON.stringify(userObj)
      console.log(userStr)
      // 设置请求体 发送请求
      xhr.send(userStr)
    })

五、 认识Promise

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

  • 同步方式:你站在门口等外卖员送来,什么事都不干,一直等着。

  • 异步方式:你点完外卖,继续干别的事(比如学习/打游戏),等外卖来了,电话通知你,然后你去取。

5.1 为什么 JavaScript 需要异步?

因为 JavaScript 是 单线程的(同一时间只能做一件事),如果你在执行一个耗时操作(比如网络请求、读取大文件),同步写法会卡住整个页面,用户无法点击、无法操作,非常糟糕。异步写法可以把“耗时的事”交给浏览器处理,不阻塞主线程,页面流畅运行

Promise语法

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

学了Promise后, 会更好的理解axios , 能够解决回调函数地狱的问题

创建Promise对象后传入的两个参数都是函数,但是二者分别是表示成功时调用的函数 和 失败时调用的函数,等待定时器异步操作完成后,屏幕会输出函数传入的字符

    // 1. 创建Promise对象
    // 参数名	类型	作用
    // resolve	函数	表示成功时调用,用来传递“成功的结果”
    // reject	函数	表示失败时调用,用来传递“失败的原因”
    const p = new Promise((resolve, reject) => {
      // 2. 执行异步操作
      setTimeout(() => {
        resolve('模拟AJAX请求-成功的结果')
        // reject(new Error('模拟AJAX请求-失败的结果'))
      }, 2000)
    })

    // 3. 获取结果
    p.then(result => {
      console.log(result)
    }).catch(error => {
      console.log(error)
    })

5.2 Promiss - 三种状态

一个Promiss对象,必然处于一下的一种状态:

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

已兑现(fulfilled) .then(回调函数):意味着,操作成功完成

已拒绝(rejected) .catch(回调函数): 意味着操作失败

请求成功时,是fulfilled状态,触发.then()

请求失败时,是rejected状态, 触发.catch()

5.3 使用Promiss + XHM 获取省份列表

当发送请求错误的时候 将error.message错误信息渲染到屏幕上

六、 封装简易的axios——获取省份列表

通过myAxios传入的对象参数进行接收,默认是GET选项

  1. 创建Promise对象
  2. 发起XHR请求,默认是GET
  3. 进行xhr.open设置请求方法和地址,来为发送请求做准备
  4. loadend 是在请求完成后触发,来进行发挥成功或者失败的函数
  5. xhr.status 是服务器响应状态,xhr.response 是服务器响应内容
  6. xhr.send()正式发起请求到服务器 
  <p class="my-p"></p>
  <script>
    /**
     * 目标:封装_简易axios函数_获取省份列表
     *  1. 定义myAxios函数,接收配置对象,返回Promise对象
     *  2. 发起XHR请求,默认请求方法为GET
     *  3. 调用成功/失败的处理程序
     *  4. 使用myAxios函数,获取省份列表展示
    */
    // 1. 定义myAxios函数,接收配置对象,返回Promise对象
    function myAxios(config) {
      return new Promise((resolve, reject) => {
        // 2. 发起XHR请求 默认是get
        const xhr = new XMLHttpRequest()
        // open 设置请求的方法和地址,为发送请求做准备
        xhr.open(config.method || 'GET', config.url)
        //  loadend 是在 请求完成 (不管成功还是失败)时触发的事件,用来统一处理响应结果。
        xhr.addEventListener('loadend', () => {
          // 3. 调用成功 / 失败 \
          // xhr.response 是服务器响应的内容。
          if (xhr.status >= 200 && xhr.status < 300) resolve(JSON.parse(xhr.response))
          else reject(new Error(xhr.response))
        })
        // 正式发起请求,把配置好的 XHR 请求发送到服务器。
        xhr.send()
      })
    }

    // 4. 使用myAxios函数 获取省份列表
    myAxios({
      url: 'http://hmajax.itheima.net/api/province'
    }).then(result => {
      console.log(result)
      document.querySelector('.my-p').innerHTML = result.list.join('<br>')
    }).catch(error => {
      console.log(error)
      document.querySelector('.my-p').innerHTML = error.message
    })


  </script>

修改myAxios函数,支持传递查询参数,就是需要传入params选项

        // 判断 有 params 选项, 携带查询参数
        if (config.params) {
          // 使用URLSearchParmas转换 并携带到url上
          const paramsObj = new URLSearchParams(config.params)
          const queryString = paramsObj.toString()
          console.log(paramsObj)
          console.log(queryString)

          // 把查询数字字符串 拼接到url?后面
          config.url += `?${queryString}`
          console.log(config.url)
        }

 起到查询作用就是要根据上面学习的URLSearchParams浏览器内置函数来将用户传入的params查询参数对象转换成字符串来坪街道url?后面

  1. 判断是否携带params对象参数
  2. 使用 URLSearchParams转换 并携带到url上 此时还是一个对象关系
  3. 将paramsObj转换成字符串queryString
  4. 进行与url拼接 
  <p class="my-p"></p>
  <script>
    /**
     * 目标:封装_简易axios函数_获取省份列表
     *  1. 定义myAxios函数,接收配置对象,返回Promise对象
     *  2. 发起XHR请求,默认请求方法为GET
     *  3. 调用成功/失败的处理程序
     *  4. 使用myAxios函数,获取省份列表展示
    */
    // 1. 定义myAxios函数,接收配置对象,返回Promise对象
    function myAxios(config) {
      return new Promise((resolve, reject) => {
        // 2. 发起XHR请求 默认是get
        const xhr = new XMLHttpRequest()

        // 判断 有 params 选项, 携带查询参数
        if (config.params) {
          // 使用URLSearchParmas转换 并携带到url上
          const paramsObj = new URLSearchParams(config.params)
          const queryString = paramsObj.toString()
          console.log(paramsObj)
          console.log(queryString)

          // 把查询数字字符串 拼接到url?后面
          config.url += `?${queryString}`
        }
        console.log(config.url)

        // open 设置请求的方法和地址,为发送请求做准备
        xhr.open(config.method || 'GET', config.url)
        //  loadend 是在 请求完成 (不管成功还是失败)时触发的事件,用来统一处理响应结果。
        xhr.addEventListener('loadend', () => {
          // 3. 调用成功 / 失败 \
          // xhr.response 是服务器响应的内容。
          if (xhr.status >= 200 && xhr.status < 300) resolve(JSON.parse(xhr.response))
          else reject(new Error(xhr.response))
        })
        // 正式发起请求,把配置好的 XHR 请求发送到服务器。
        xhr.send()
      })
    }

    // 4. 使用myAxios函数 获取省份列表
    myAxios({
      url: 'http://hmajax.itheima.net/api/area',
      params: {
        pname: '湖北省',
        cname: '武汉市'
      }
    }).then(result => {
      console.log(result)
      document.querySelector('.my-p').innerHTML = result.list.join('<br>')
    }).catch(error => {
      console.log(error)
      document.querySelector('.my-p').innerHTML = error.message
    })


  </script>

七、 注册账号——支持传递请求体数据 data选项

        // 判断是否有data选项 携带请求体
        if (config.data) {
          // 数据转换类型 在send中发送
          const jsonStr = JSON.stringify(config.data)
          // 请求体数据类型标记
          xhr.setRequestHeader('Content-Type', 'application/json')
          xhr.send(jsonStr)
        }
        // 正式发起请求,把配置好的 XHR 请求发送到服务器。
        else xhr.send()
      })

  1. 提交数据到服务器,用POST方法进行提交,然后携带data对象数据
  2. 要将data对象数据转换成JSON字符串
  3. 标记数据传输到服务器的数据类型是json格式
  4. 然后正式发起请求 xhr.send(josnStr)
  <button class="reg-btn">注册用户</button>
  <script>
    /**
     * 目标:封装_简易axios函数_获取省份列表
     *  1. 定义myAxios函数,接收配置对象,返回Promise对象
     *  2. 发起XHR请求,默认请求方法为GET
     *  3. 调用成功/失败的处理程序
     *  4. 使用myAxios函数,获取省份列表展示
    */
    // 1. 定义myAxios函数,接收配置对象,返回Promise对象
    function myAxios(config) {
      return new Promise((resolve, reject) => {
        // 2. 发起XHR请求 默认是get
        const xhr = new XMLHttpRequest()

        // 判断 有 params 选项, 携带查询参数
        if (config.params) {
          // 使用URLSearchParmas转换 并携带到url上
          const paramsObj = new URLSearchParams(config.params)
          const queryString = paramsObj.toString()
          console.log(paramsObj)
          console.log(queryString)

          // 把查询数字字符串 拼接到url?后面
          config.url += `?${queryString}`
        }
        console.log(config.url)

        // open 设置请求的方法和地址,为发送请求做准备
        xhr.open(config.method || 'GET', config.url)
        //  loadend 是在 请求完成 (不管成功还是失败)时触发的事件,用来统一处理响应结果。
        xhr.addEventListener('loadend', () => {
          // 3. 调用成功 / 失败 \
          // xhr.response 是服务器响应的内容。
          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)
        }
        // 正式发起请求,把配置好的 XHR 请求发送到服务器。
        else xhr.send()
      })
    }

    document.querySelector('.reg-btn').addEventListener('click', () => {
      // 4. 使用myAxios函数 获取省份列表
      myAxios({
        url: 'http://hmajax.itheima.net/api/register',
        method: 'POST',
        data: {
          username: 'wshhaaaaa12w~',
          password: '1234567'
        }
      }).then(result => {
        console.log(result)
      }).catch(error => {
        console.log(error)
      })
    })
  </script>

八、 天气预报案例

 一些渲染操作都是非常简单的,就最后一步原生js 响应式输入,只要得到了当前的输入框,然后add~input就能做到响应式!!!

document.querySelector('.search-city').addEventListener('input', e => {
  console.log(e.target.value)
})
/**
 * 目标1:默认显示-北京市天气
 *  1.1 获取北京市天气数据
 *  1.2 数据展示到页面
 */

// 获取并渲染城市天气函数
const getWeather = async (cityCode) => {
  // 获取天气数据
  const response = await axios('http://hmajax.itheima.net/api/weather', { params: { city: cityCode } })
  console.log(response.data)
  const data = response.data.data
  // 展示数据
  document.querySelector('.dateShort').innerHTML = data.date
  document.querySelector('.dateLunar').innerHTML = data.dateLunar
  document.querySelector('.area').innerHTML = data.area
  // 当天的气温
  const nowWStr = `
  <div class="tem-box">
        <span class="temp">
          <span class="temperature">${data.temperature}</span>
          <span>°</span>
        </span>
      </div>
      <div class="climate-box">
        <div class="air">
          <span class="psPm25">${data.psPm25}</span>
          <span class="psPm25Level">${data.psPm25Level}</span>
        </div>
        <ul class="weather-list">
          <li>
            <img src="${data.weatherImg}" class="weatherImg" alt="">
            <span class="weather">${data.weather}</span>
          </li>
          <li class="windDirection">${data.windDirection}</li>
          <li class="windPower">${data.windPower}</li>
        </ul>
      </div>
      `
  document.querySelector('.weather-box').innerHTML = nowWStr

  // 当天的天气
  const twObj = data.todayWeather
  const todayWStr = `
    <div class="range-box">
        <span>今天:</span>
        <span class="range">
          <span class="weather">${twObj.weather}</span>
          <span class="temNight">${twObj.temNight}</span>
          <span>-</span>
          <span class="temDay">${twObj.temDay}</span>
          <span>℃</span>
        </span>
      </div>
      <ul class="sun-list">
        <li>
          <span>紫外线</span>
          <span class="ultraviolet">${twObj.ultraviolet}</span>
        </li>
        <li>
          <span>湿度</span>
          <span class="humidity">${twObj.humidity}</span>%
        </li>
        <li>
          <span>日出</span>
          <span class="sunriseTime">${twObj.sunriseTime}</span>
        </li>
        <li>
          <span>日落</span>
          <span class="sunsetTime">${twObj.sunsetTime}</span>
        </li>
      </ul>
    </div>
  `
  document.querySelector('.today-weather').innerHTML = todayWStr

  // 七日天气预报数据展示
  const dayForecast = data.dayForecast
  const dayForecastStr = dayForecast.map(item => {
    return `
      <li class="item">
          <div class="date-box">
            <span class="dateFormat">${item.dateFormat}</span>
            <span class="date">${item.date}</span>
          </div>
          <img src="${item.weatherImg}" alt="" class="weatherImg">
          <span class="weather">${item.weather}</span>
          <div class="temp">
            <span class="temNight">${item.temNight}</span>-
            <span class="temDay">${item.temDay}</span>
            <span>℃</span>
          </div>
          <div class="wind">
            <span class="windDirection">${item.windDirection}</span>
            <span class="windPower">&lt;${item.windPower}</span>
          </div>
        </li>
    `
  }).join('')

  document.querySelector('.week-wrap').innerHTML = dayForecastStr
}

// m默认进入网页 - 就要获取天气数据(北京城市编码就是 ‘110100’)
getWeather('110100')

// 搜索城市列表
document.querySelector('.search-city').addEventListener('input', async e => {
  console.log(e.target.value)
  const response = await axios('http://hmajax.itheima.net/api/weather/city', { params: { city: e.target.value } })
  const cityStr = response.data.data.map(item => {
    return `<li class="city-item">${item.name}</li>`
  }).join('')
  document.querySelector('.search-list').innerHTML = cityStr
  document.querySelector('.search-list').addEventListener('click', ee => {
    const num = response.data.data.filter(item => {
      return item.name === ee.target.innerHTML
    })
    console.log(num)
    if (num.length > 0) getWeather(num[0].code)
  })
})

最后获得城市的code值优化,只需要在渲染的同时加上自定义data-code属性就好! 

  const cityStr = response.data.data.map(item => {
    return `<li class="city-item" data-code="${item.code}">${item.name}</li>`
  }).join('')
  document.querySelector('.search-list').innerHTML = cityStr
  document.querySelector('.search-list').addEventListener('click', e => {
    console.log(e.target.dataset.code)
    getWeather(e.target.dataset.code)
  })

总结不易~ 本章节对我有很大的收获,希望对你也是!!!


网站公告

今日签到

点亮在社区的每一天
去签到