ajax和XMLHttpRequest以及fetch

之前我们直接用了fetch发送请求,很简单,但是fetch的底层逻辑我们并不理解,比如现在你跟我说fetch是封装好了的xhr和promise,我会很蒙,promise一个异步处理器,成功自动调用resolve失败了就是reject,没什么问题,promise实例对象我们可以用自带的方法.then去获取response对象,.catch去获取错误,没什么问题。但是xhr和ajax说真的一点不认识。

1.Ajax和XHR是什么?

我们先来张图片。

看起来很乱,但对我来说我写的时候脑子在发光,之前学计算机觉得键盘敲敲打打的就行,但是自从画了图,写了一些草稿就觉得纸和笔真的牛,梳理思路的时候还是最原始的方式可以引发自己的大脑思考。

那么我现在给你描述一下什么是ajax,我现在的理解是一个过程。首先我们在浏览器输入网页,我们向谁去获取资源呢,也就是当我们输入了一个关于网页客户端的关键字,浏览器匹配响应的url,然后通过http去向服务器发送请求获取资源,服务器响应资源给浏览器展示。

但是如果我们想渲染了页面之后,我们希望比如看b站视频的时候点击其他视频,也就是在网页不重新渲染的情况下局部更新页面,通过js触发事件告诉浏览器,因为js不具有网络通信的能力,所以浏览器和js代码就需要一个规范,也就是一个协议。

当我们写了这个js代码浏览器能识别我们想要发送一个请求给服务器。那么XMLHttpRequest这个东西就出现了。简称xhr小黄人吧,那么思路清晰了,我们用xhr这个规范写的代码,浏览器可以认出来,因为我们直接有了协议规范。

现在就是js---xhr-->浏览器---http--->服务器。那么我们就要知道这个xhr规范怎么规范的。首先xhr是一个构造函数。 哦一下子清晰了,是构造函数,那么就可以new直接拿来用,拿来用了,那么里面的方法肯定是现成的,我们看看文档怎么说,首先send是发送,open是包含请求方法,url,是否异步(falsetrue),好像可以了。但是这样我们好像只是发了,我们怎么知道回来没有,或者说报错了,哪里有问题呢,我们再看一下文档。

哦看到了一个readystate,也就是说我们可以根据这个属性的值 01234去判断请求的过程,0未调用open,1表示调用了open 但是没send,2表示发送但是服务器没响应,3表示部分响应,4表示全部响应。这是我们js这边的状态吗,服务器也有啊,比如200 400 404 ,我们记得200 是成功了,那么我们只需要给这个构造函数添加一个方法根据这些状态判断然后输出或者拿我们的数据就ok了,当获取返回的数据用js对DOM进行操作实现局部页面的刷新,这就是Ajax。

但以上只是我自己的看法,我们需要去官方看标准。

什么是 AJAX

AJAX 是 "Asynchronous JavaScript And XML" 的缩写 它指的不是某个函数,也不是某个类,而是一整套机制/技术组合:

使用 JavaScript,发起异步请求(通常用 XMLHttpRequestfetch),与服务器交换数据(可以是 JSON、XML、HTML、Text),无需刷新页面即可更新网页内容(局部刷新)。

应该是差不多,但是只是这样很难记住啊,写一些代码吧。

javascript 复制代码
import React from 'react'

export default function App() {
    const divRef = React.useRef()
    React.useEffect(() => {
        let xhr = new XMLHttpRequest()
        xhr.open('get', 'http://localhost:1337/api/students', true)
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    divRef.current.innerHTML = xhr.responseText
                } else {
                    console.log('请求出错了' + xhr.status);
                }
            }
        }
        xhr.send()
    }, [])
    return (
        <div ref={divRef}>App</div>
    )
}

大师悟了啊,new调用构造函数拿过来直接用,里面的实例方法open send直接用啊,然后添加函数通过自带的状态码去判断然后修改局部DOM,多简单明了。现在我们就实现了ajax这个流程而且认识了什么是xhr。

2.Promise封装xhr

你知道我想说什么,fetch是什么东西,xhr和promise结合。那我们就看看promise和xhr结合是什么情况。

javascript 复制代码
import React, { useEffect } from 'react'

export default function App() {
    const divRef = React.useRef()
    function ajaxPromiss() {
        let promise = new Promise((resolve, reject) => {
            let xhr = new XMLHttpRequest()
            xhr.open('get', 'http://localhost:1337/api/students', true)
            xhr.onreadystatechange = () => {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                        resolve(xhr.responseText)
                    } else {
                        reject('报错了' + xhr.status)
                    }
                }
            }
            xhr.send()
        })
        return promise
    }
    useEffect(() => {
        ajaxPromiss()
            .then((data) => {
                const newData = JSON.parse(data)
                divRef.current.innerHTML = JSON.stringify(newData.data)
            })
            .catch((e) => {
                divRef.current.innerHTML = e
            })
    }, [])

    return (
        <div ref={divRef}>App</div>
    )
}

我们发现只是在返回结果的时候用promise的resolve和reject,其实没什么大区别,只是用了这两个好哥们,然后.then.catch去获取这两好哥们给我们的数据,就这么简单。那么我们用fetch呢?

javascript 复制代码
    function fetchData() {
        return fetch('http://localhost:1337/api/students')
            .then((res) => {
                if (!res.ok) {
                    throw new Error('加载出错' + res.status)
                }
                return res.json()
            })
    }

轻便,简洁,好理解,人性化,甚至省去了get,以及确定是否异步的那个布尔值,直接发,还自带promise好兄弟(resolve和reject),这就是fetch,一个浏览器为我们封装好了的XMLHttpRequest+Promise,并且自带处理了一些细节比如响应格式和请求状态。那么axios这个库呢,无非就是比fetch更加直观,更加多样,更好用。。实际上用的时候知道原理,会用的更得心应手。这也是为什么需要学原理的原因。