JavaScript 网络编程(二)

176 阅读9分钟

JavaScript 网络编程(二)

AJAX 发送网络请求

发送网络请求基本流程

  • 发送AJAX网络请求的步骤

    • 第一步创建网络请求的 AJAX 对象(XMLHttpRequest
    • 第二步监听XMLHttpRequest 状态的变化,或者说 onload 事件(请求完成时触发请求)
    • 第三步配置网络请求(通过我们的 open 方法实现打开)
    • 第四步发送send网络请求
  • 该请求方式的话是可以实现的是我们的发送 JSON | XML | HTML | text 文本等格式的发送和接收数据的

// 开始实例化发送请求的实例对象
const xhr = new XMLHttpRequest()
​
// 开始进行监听对象的改变(宏任务中的,发送了实时的请求后,就来实现回调这个函数)
xhr.onreadystatechange = () => {
    // 先判断响应状态码
    if (xhr.readyState === 4 && xhr.status === 200) {
        console.log(JSON.parse(xhr.response))
        console.log(JSON.parse(xhr.response)?.data?.banner)
    }
}
​
// 开始实现网络请求的配置
/**
 * method 实现请求的方式
 * url 发送请求的地址
 */
xhr.open("GET", "url")
​
// 实现发送网络请求
xhr.send()

XMLHttpRequest readyState状态监听

在我们实现发送网络请求的时候,状态的变化一共是五次的变化,但是有一次的状态变化,我们是可以不用进行监听的

状态描述
0unsent代理被创建了,但是还没有使用 open() 方法
1openedopen() 方法已经被调用
2headers_receivedsend() 方法已经被调用,并且头部和状态已经可获得
3loading下载中,responseText 属性已经包含了部分数据
4done下载操作已经完成
  • 所以说我们现在就可以使用我们的 xhr.readyState == XMLHtttpResponse.DONE 来进行我们的网络请求的判断,满足条件

    • 我们直接进入下一步的操作,否则终止操作即可
  • 同时我们在打开请求的时候,我们的 open 方法可以传递三个参数

    • 第一个参数就是发送请求的方式

    • 第二个参数就是发送请求的后端数据库服务器 API 接口

    • 第三个参数就是是否执行异步请求的操作(默认打开的)

      • 异步请求代码执行上来观看的话,就是在我们请求后续的代码是不会被阻塞的
      • 同步请求代码执行上来观测的话,就是在我们的请求操作获得结果后,后续的代码才被执行,后续代码直接被阻塞

XMLHttpRequest 其他的监听

  • 我们在平时开发中,常常使用的监听是我们的 xhr.onreadystatechange ,但是的话实际上还有其他的监听模式的
监听模式监听描述
loadstart请求开始时候的监听
progress一个数据包到达的监听
abort取消请求的方法
error发生连接错误的监听
load请求成功的监听
timeout请求超时被取消请求的监听
loadend在 load `errortimeoutabort` 触发的监听

XMLHttpRequest 响应数据和响应类型设置

  • 如何实现获取我们的响应数据呐???

    • xhr.response 就可以就可以实现获取得到我们的响应数据了,任何数据格式支持
    • xhr.responseText 专门用来实现获取的是我们的文本类型的数据
    • xhr.responseXML 专门用来实现获取的是 XML 的数据格式
  • 但是我们在实现 open() 打开请求的时候,实现的请求配置,就可以在 open() 方法之前实现设置

    • xhr.responseType

    • 该属性默认的是设置为的 text 普通的文本,所以说这个时候不管是服务器返回的数据格式是什么

      • 都是会被解析为 字符串类型的
    • 但是如果想一开始就是接收的是我们的 JSON 数据的格式

      • 我们就需要实现的是我们的通过设置返回值类型来限制我们的解析的
      • xhr.responseType = "json"
      • 这样实现进行后,我们就不用在后续通过 JSON.parse 来进行解析字符串了
      • 获取得到的数据直接通过 JSON 格式进行解析返回值数据
    • responseType 可以设置的属性类型MDN 文档阅读

  • 我们现在的话,我们常见的服务器响应的数据类型都是 JSON 数据格式了

XMLHttpRequest 获取http 响应状态

  • 获取我们的响应请求 Http 的状态码就是通过的是我们的 xhr.status 来实现获取响应状态码的

  • 一般进行后续的操作的话,我们直接使用 200 进行判断即可,200代表的是请求成功的标识

  • 通过和 200 的请求对比,我们后面在实现对后续的操作即可

  • 这里来讲一个都比较熟悉的,当我们发送请求的时候,如果出现 404 NOT FOUND 的提示,那么就是服务器资源没有找到

    • 懂的都懂,哈哈哈!!!都干过类似的事情的!!!
  • 这里我们科普一下,后续发送网络请求的时候可能出现跨域问题,先看别人的博客吧

  • xhr.status >= 200 && xhr.status < 300 进行 http 请求状态码的判断

XMLHttpRequest 客户端的传参方式

  • GET / POST 请求传递参数的形式

    • GET 请求的 query 参数的形式实现传递

      • 就是实现的是通过我们的 ? 实现将我们的请求参数和 url 实现拼接
      • 然后通过 & 将我们的每一个参数进行连接
      • 传递的数据是用的是我们的键值对的形式传输的,使用的是 = 进行连接即可
      • https://www.example.com/get?name=76433&age=18&githubName=juwenzhang/
      •   xhr.open("GET", "https://www.example.com/get?name=76433&age=18&githubName=juwenzhang/")
        
    • POST 请求的 x-www-form-urlencoded 格式实现传递数据

      •   xhr.open("POST", "https://www.example.com/get/")
          ​
          // 实现发送网络请求,开始发送我们的请求体设置
          xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")  // 告知服务器我们传递的数据类型是什么
          xhr.send("name=76433&age=18&githubName=juwenzhang/")
        
    • POST 请求的 FormData 格式进行传递数据

      • FormData 实际上的话是一个对象,和 form 表单连同使用

      • form 表单的实现形式的话,可以通过获取得到我们的 form 表单中的每个值,然后进行拼接成 urlencoded 即可

      • 或者说通过我们的 formdata 实现发送数据也是可以的

        • new FormData(value)
        •   xhr.open("POST", "https://www.example.com/get/")
            ​
            // 实现发送网络请求,开始发送我们的请求体设置
            const formData = new FormData(document.querySelectorAll('form'))
            xhr.send(formData)
          
    • POST 请求的 JSON 格式传递数据

      •   xhr.open("POST", "https://www.example.com/get/")
          ​
          // 实现发送网络请求,开始发送我们的请求体设置
          xhr.send(JSON.stringify({
              name: "76433",
              age: 18
          }))
        
  • 直接来两个具体的样例吧:

    • 这个代码使用的技术栈比较老了,使用的是 Django + Jquery 来实现的,而且还是一个烂尾的个人项目
    • 仓库地址
    • 下面的两种方式分别是JSON 传递数据的格式和 FormData 传递数据的格式

image-20241129095843215.png

image-20241129095914947.png

AJAX 网络请求封装

/**
 * 自定义封装网络请求
 * 实际上含有很多的好的封装的网络请求库,这里只是微微练习罢了
 * @param {string} url
 * @param {string} method
 * @param {Object} headers
 * @param {number} timeout
 * @param {Object} data
 * @param {"" | "arraybuffer" | "blob" | "document" | "json" | "text"} dataType
 * @param {boolean} is_async
 * @param {Function} success
 * @param {Function} failure
 */
function http_request_MyAjax({url, method = "GET", headers = {}, timeout = 3000, data = {},
                               dataType = "json", is_async = true, success, failure} = {}) {
​
    // 返回我们的 Promise 来实现处理返回值结果
    return new Promise((resolve, reject) => {
        try {
            const xhr = new XMLHttpRequest()
​
            // 监听事件
            xhr.onload = function() {
                if (xhr.status >= 200 && xhr.status < 300) {
                    success&& success(xhr.response)
                    resolve({status: xhr.status, statusText: xhr.statusText, response: xhr.response})
                } else {
                    failure&& failure({status: xhr.status, statusText: xhr.statusText})
                    reject({status: xhr.status, statusText: xhr.statusText, response: xhr.response})
                }
            }
​
            // 配置信息
            xhr.responseType = dataType
            xhr.timeout = timeout
​
            // 监听网络超时
            xhr.ontimeout = function() {
                // 定时取消我们的网络请求
                const timer = setTimeout(function() {
                    xhr.abort()
                }, 3000)
                reject({status: xhr.status, statusText: xhr.statusText, message: "请求超时 Timeout"})
                clearTimeout(timer)
            }
​
            // 区分 Get 和 Post 发送请求的方式
            if (method.toUpperCase() === "GET") {
                // for (const [key, value] of Object.entries(data)) {
                //     url = url + "?" + key + "=" + value
                // }
                url += Object.keys(data).map(key =>
                    `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`).join("&")
                xhr.open(method, url, is_async)
                xhr.send()
            } else {
                xhr.open(method, url, is_async)
                if (headers) {
                    // for (const [key, value] of Object.entries(data)) {
                    //     xhr.setRequestHeader(`${key}`, `${value}`)
                    // }
                    Object.entries(headers).forEach(([key, value]) =>
                        xhr.setRequestHeader(key, `${value}`))
                }
                xhr.send(JSON.stringify(data))
            }
        } catch (e) {
            reject(e)
        }
    })
}

上面的代码就是我们自己的网络请求封装函数库,我们不用太注意这个吧,因为全网对网络请求进行封装的博客数不胜数

理解思想即可,这个标题部分看着玩玩就行了!!!

注意自己在封装的时候,不用监听 onerror 事件的,如果监听了,不用使用 try...catch... 语句来进行捕获异常

使用了 try...catch... 来实现捕获了异常,这个时候就不用使用通过 onerror 语句来捕获异常了,二者不可兼得

同时这里的话,我们还是通过的是 Promise 来进行处理的我们的结果,这是一个很好的开发思维模式,代码的可读性增强很多很多

Fetch 和 Fetch API

  • 该请求方式的话是早期的 XHR 的替代方案

  • 其返回值是一个 Promise

    • 请求发送成功的时候,调用 resolve 回调 then 即可
    • 请求发送失败的时候,调用reject 回调catch 即可
  • 在后期的话,自己也不要闹一个笑话,就是 axios 和 ajax 有什么区别???

    • 以后自己问了这个问题后,给自己的一个大巴嘴子
    • axios 只是对原生的 ajax 请求进行了一定封装的网络请求 JS 库
    • 一个封装的网络请求库是无法和我们的原生的东西相比的,不在一个维度
    • FetchAjax 都是属于浏览器默认给我们提供的原生的网络请求的方式,以后要问就问这二者的不同 😄 😄 😄

Fetch函数的基本使用

  • Fetch(input, [init])

  • input 定义需要获取的资源地址url,这里的话是可以输入对应的 url 地址或者说是 一个 Request 对象

  • init 表示的就是我们的初始化参数

    • method 请求方式的初始化
    • headers 配置型文件的定义
    • body 请求的 body 信息

先来一个 Get 请求

// 使用我们的 Fetch 发送一个get 请求
/**
 * 为实现优化的fetch 请求
 */
fetch("https://www.google.com/search/").then((response) => {
    console.log(response)
    // 首先实现获取得到我们的 response
    const res = response
​
    // 获取具体的返回值结果
    res.json().then(data => {
        console.log(data)
    })
}).catch((error) => {
    console.log(error)
})
​
​
/**
 * 优化后的fetch 请求
 * @return {Promise<void>}
 */
async function getDate() {
    const res = await fetch("https://www.google.com")
    const data = await res.json()
    console.log(data)
}

实现一个 Post 请求

async function getData() {
    const formData = new FormData()
    formData.append('email', document.getElementById('email').value)
    formData.append('password', document.getElementById('password').value)
    formData.append('password_confirmation', document.getElementById('password_confirmation').value)
    const res = await fetch("https://jsonplaceholder.typicode.com/posts/1", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        // 传递参数的时候,向服务器提交的数据
        body: JSON.stringify(formData)
    })
​
    console.log(res.status, res.statusText, res.ok)
​
    return await res.json()
}
​
getData().then((res) => {
    console.log(res)
}).catch((err) => {
    console.log(err)
})
  • 这里的话,我们个人的话可以实现的是拓展一下上传文件的前端时如何做的
  • 同时我们实现上传文件的时候,一般使用的是 xhr 来实现文件的上传,因为 fetch 请求是不会监听文件上传的进度的
  • 在文件上传的过程中,我们前端实现的操作实际上是很少的,做的最多的还是我们的后端
  • 上传我们的文件的时候,基本上都是使用的是我们的 formData 对象来实现的,这个时候的是: multipart/formdata
  • 前端文件上传基本流程简介