Ajax(一)

162 阅读7分钟

AJAX

  • 在 js 中有内置的构造函数来创建 ajax 对象
  • 创建 ajax 对象以后,我们就使用 ajax 对象的方法去发送请求和接受响应

一、创建ajax

1、创建一个ajax

     const xhr = new XMLHttpRequest()
  • 上面就是有了一个 ajax 对象
  • 我们就可以使用这个 xhr 对象来发送 ajax 请求了

2、配置ajax

  • 向谁发送请求?以什么方式发送请求?这两个问题的答案,在接口文件中
  • xhr.open('以什么方式请求,目前开发中只有post/get', '向谁发送请求', '第三个参数暂时不讲')
       xhr.open('GET', 'http://localhost:8888/test/first')

3、发送ajax

    xhr.send()

4、监听请求完成的时间

  • 请求完成的时候,说明数据已经给到我们前端,我们开始使用
      xhr.onload = function () {
            //请求完成的时候,说明数据已经给到我们前端,我们开始使用
      console.log(xhr.responseText)
     }

二、ajax的异步问题

  • 其实就是 配置 ajax 的第三个参数,这个参数默认是 true 表示当前的请求是 异步

1、异步: 1 2 3 4

    1. 创建 (同步)
    1. 配置 (同步)
    1. 发送 (异步)
    1. 监听 (同步)
       // 异步: 1 2 3 4
       // 1. 创建一个 ajax
       const xhr = new XMLHttpRequest()
       // 2. 配置 ajax
       xhr.open('GET', 'http://localhost:8888/test/first')
       // 3. 发送 ajax
       xhr.send()
       // 4. 监听 ajax 完成
       xhr.onload = function () {
           console.log(xhr.responseText)
       }

2、异步: 1 2 4 3

    1. 创建 (同步)
    1. 配置 (同步)
    1. 监听 (同步)
    1. 发送 (异步)
        // 异步: 1 2 4 3
       // 1. 创建一个 ajax
       const xhr = new XMLHttpRequest()
       // 2. 配置 ajax
       xhr.open('GET', 'http://localhost:8888/test/first')
       // 4. 监听 ajax 完成
       xhr.onload = function () {
           console.log(xhr.responseText)
       }
       // 3. 发送 ajax
       xhr.send()

3、同步: 1 2 4 3

    1. 创建 (同步)
    1. 配置 (同步)
    1. 监听 (同步)
    1. 发送 (异步)
       // 同步: 1 2 4 3
       // 1. 创建一个 ajax
       const xhr = new XMLHttpRequest()
       // 2. 配置 ajax
       xhr.open('GET', 'http://localhost:8888/test/first', false)
       // 4. 监听 ajax 完成
       xhr.onload = function () {
           console.log(xhr.responseText)
       }
       // 3. 发送 ajax
       xhr.send()

4、同步: 1 2 3 4

    1. 创建 (同步)
    1. 配置 (同步)
    1. 发送 (异步)
    1. 监听 (同步)
       // 同步: 1 2 3 4
       // 1. 创建一个 ajax
       const xhr = new XMLHttpRequest()
       // 2. 配置 ajax
       xhr.open('GET', 'http://localhost:8888/test/first', false)
       // 3. 发送 ajax
       xhr.send()
       // 4. 监听 ajax 完成
       xhr.onload = function () {
           /**
            *  因为 JS 加载 到 xhr.onload 的代码时,
            *      请求已经完全结束了
            *  所以 当前函数不会被触发
           */
           console.log(xhr.responseText)
       }

三、ajax的状态码

  • 利用了一个数字表明 ajax 当前运行到那一步了
    • 0: ajax 创建完毕
    • 1: ajax 配置完毕
    • 2: ajax 发送完毕 (后端已经对请求做出响应, 并把请求返回给 浏览器)
    • 3: 浏览器开始解析后端返回的内容, 如果返回的数据比较少, 那么此时就可以使用数据了, 但是有可能没有解析完毕, 数据不完整, 所以不推荐在这里使用数据
    • 4: 浏览器的解析的内容已经全部处理完毕, 我们可以开始使用数据了
     const xhr = new XMLHttpRequest()
        // console.log(xhr.readyState) // 0

        xhr.open('GET', 'http://localhost:8888/test/first')
        // console.log(xhr.readyState) // 1

        xhr.send()
        // console.log(xhr.readyState) // 因为异步的问题所以还是 1

        // xhr.onreadystatechange = function () {
        //     /**
        //      *  监听状态码发生变化的时候执行
        //      *      当前会执行3次
        //      *          1 变 2
        //      *          2 变 3
        //      *          3 变 4
        //     */
        //     if (xhr.readyState === 2) { console.log('2 ---> ajax 发送完毕 (后端已经对请求做出响应, 并把请求返回给 浏览器)') }
        //     if (xhr.readyState === 3) { console.log('3 ---> 浏览器开始解析数据, 但是有可能没有解析完毕') }
        //     if (xhr.readyState === 4) { console.log('4 ---> 浏览器解析完毕数据', xhr.responseText) }
        // }

        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                console.log('4 ---> 浏览器解析完毕数据', xhr.responseText)
            }
        }

        xhr.onload = function () {
            console.log('请求完毕 onload 加载: ', xhr.responseText)
        }
        // console.log(xhr.readyState) // 因为异步的问题所以还是 1

四、http协议

  • 当前协议规定 请求 只能是 前端发起的, 并且在传输过程中, 只能传递字符串
      1. 建立连接:览器和服务器建立连接
      1. 发送请求:如要携带一些参数, 那么需要以请求报文的形式传递,览器进行组装, 我们只需要传递一些对应的信息
      1. 接收响应:根据请求报文内的相关信息, 确定现在需要做什么事情, 并把必要的数据进行返回;返回的内容需要放在 响应报文中
      1. 关闭连接: 浏览器和服务器的连接到此结束
  • 综上所述, 我们的每一个请求都是完全独立的, 前一个请求和后一个请求没有任何关联哪怕两次请求之间的间隔很短, 那也没有任何关联
  • HTTP协议请求状态码:
    • 100~199: 连接正在进行中(持续进行)
    • 200~299: 表明连接成功
    • 300~399: 表明重定向
    • 400~499: 表明前端错误 (没权限/传参错误)
    • 500~599: 表明后端错误

五、请求方式的差异

  • GET:获取的含义
  • POST:提交的含义 请求方式最大的差异就是传参

1.传参的方式

  • GET:直接在地址后边拼接参数即可, 格式类似于以前的 查询字符串
  • POST:在请求体内书写 (其实就是 xhr.send(这小括号内))

2.传参的大小

  • GET:2kb 左右
  • POST:原则上没有限制, 但是我们可以在后端添加限制

3.传参的安全性

  • GET:明文传输, 相对不安全
  • POST:密文传输, 相对安全

4.传参的格式

  • GET:传递的是查询字符串格式
  • POST:原则上也没有限制, 但是传参的时候需要通过 content-type 指定我们传参的格式

六、GET/POST测试请求

1.GET测试请求

        const xhr = new XMLHttpRequest()
        xhr.open('GET', 'http://localhost:8888/test/first')
        xhr.send()
        xhr.onload = function () {
            console.log(xhr.responseText)
        }
        const xhr = new XMLHttpRequest()
        xhr.open('GET', 'http://localhost:8888/test/second')
        xhr.send()
        xhr.onload = function () {
            const res = JSON.parse(xhr.responseText)
            if (res.code === 0) {
                console.log('此时可能请求失败');
                return
            }
        如果代码能够运行到这里, 说明请求成功, 我们可以做成功后的一些事情
            console.log('此时请求完毕',res);
        }
        const xhr = new XMLHttpRequest()
        // xhr.open('GET', 'http://localhost:8888/test/third?name=QF006&age=18')
        // xhr.open('GET','http://localhost:8888/test/third?name=QF008')
        xhr.open('GET', 'http://localhost:8888/test/third?age=18')
        xhr.send()
        xhr.onload = function () {
            const res = JSON.parse(xhr.responseText)
            if (res.code === 0) {
                console.log('此时请求失败');
                return
            }
            // 如果代码能够运行到这里, 说明请求成功, 我们可以做成功后的一些事情
            console.log('此时请求完毕', res);
        }

2.POST测试请求

  • post 的参数一定书写在 xhr.send() 的小括号中,并且 我们也可以传递查询字符串
    • 如果是 post 方式, 并且需要携带参数, 那么需要加一步 设置请求头
    • 参数是JSON字符串 xhr.setRequestHeader('content-type', 'application/json')
    • 参数是查询字符串 xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
        const xhr = new XMLHttpRequest()
        xhr.open('POST', 'http://localhost:8888/test/fourth')
        xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
        xhr.send('name=王慧雨&age=18')
        xhr.onload = function () {
            const res = JSON.parse(xhr.responseText)
            console.log('请求成功了', res)
        }

七、案例

1、分页

     <script>
        // 0. 获取标签
        const myUl = document.querySelector('ul')
        const prev = document.querySelector('.prev')
        const total = document.querySelector('.total')
        const next = document.querySelector('.next')
        const select = document.querySelector('select')

        // 0. 全局变量
        let currentPage = 1 // 默认在第一页
        let pageSize = 4    // 默认展示4条
        let totalNum = 0    // 存储总页码

        // 0. 封装一个渲染函数
        function fn(data) {
            // const list = data.list
            const { list } = data

            // 0.1 将数据渲染到页面
            myUl.innerHTML = list.reduce(function (prev, item) {
                return prev + `
                    <li>
                        <img src="${item.img_big_logo}" alt="">
                        <p>${item.title}</p>
                    </li>
                `
            }, '')

            // 0.2 改变页码
            totalNum = data.total
            total.innerHTML = currentPage + ' / ' + totalNum

            // 0.3 调整按钮样式
            prev.className = currentPage === 1 ? 'prev disable' : 'prev'
            next.className = currentPage === totalNum ? 'next disable' : 'next'
        }

        // 0. 封装一个请求函数
        function getData() {
            // 0.1 创建 ajax
            const xhr = new XMLHttpRequest()

            // 0.2 配置 ajax
            // xhr.open('GET', 'http://localhost:8888/goods/list?current=' + currentPage + '&pagesize=' + pageSize)
            xhr.open('GET', `http://localhost:8888/goods/list?current=${currentPage}&pagesize=${pageSize}`)

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

            // 0.4 监听请求是否加载完毕
            xhr.onload = function () {
                const res = JSON.parse(xhr.responseText)

                if (res.code !== 1) {   // 拦截错误
                    return alert(res.message)
                }

                // 代码运行到这个位置说明成功请求到数据
                fn(res)
            }
        }

        // 1. 打开页面直接调用请求函数, 请求到数据, 并渲染页面
        getData()

        // 2. 点击上一页或者下一页
        prev.onclick = function () {
            if (currentPage === 1) return
            currentPage--
            getData()
        }
        next.onclick = function () {
            if (currentPage === totalNum) return
            currentPage++
            getData()
        }

        // 3. 改变每页渲染数量
        select.onchange = function () {
            pageSize = select.value - 0
            currentPage = 1
            getData()
        }

八、封装AJax

    function objToStr(obj) {
    let str = "";
    for (let key in obj) {
        const value = obj[key];
        str += `${key}=${value}&`;
    }
    str = str.slice(0, str.length - 1);
    return str;
}

function newFn(baseUrl) {
    return function myAjax(options) {
        // 1. 完成参数校验
        if (options.url === undefined)
            throw new Error("url 为必传项, 您必须传递");
        if (
            !(
                options.method === undefined ||
                /^(get|post)$/i.test(options.method)
            )
        ) {
            throw new Error(
                "method 的值 要么为 get 要么为 post, 并且不区分大小写"
            );
        }
        if (
            !(
                options.async === undefined ||
                Object.prototype.toString.call(options.async) ===
                    "[object Boolean]"
            )
        ) {
            throw new Error("async 的值 只能为布尔值, 默认为 true");
        }
        if (
            !(
                options.data === undefined ||
                typeof options.data === "string" ||
                Object.prototype.toString.call(options.data) ===
                    "[object Object]"
            )
        ) {
            throw new Error(
                "data 的值 可以为字符串也可以为对象, 默认为 空字符串"
            );
        }
        if (
            !(
                options.header === undefined ||
                Object.prototype.toString.call(options.header) ===
                    "[object Object]"
            )
        ) {
            throw new Error("header 必须传递一个对象, 也可以不传");
        }
        if (
            !(
                options.dataType === undefined ||
                /^(string|json)$/i.test(options.dataType)
            )
        ) {
            throw new Error("dataType 目前仅支持 string 或 json, 默认为 json");
        }

        // 2. 处理参数的默认值
        const _options = {
            url: baseUrl + options.url,
            method: options.method || "GET",
            async: options.async ?? true,
            data: options.data || "",
            header: {
                "content-type": "application/x-www-form-urlencoded",
                ...options.header,
            },
            // 2.x 处理 dataType 的默认值
            dataType: options.dataType || "json",
        };
        if (
            Object.prototype.toString.call(_options.data) === "[object Object]"
        ) {
            _options.data = objToStr(_options.data);
        }

        // 3. 封装发送请求
        _options.url = /^get$/i.test(_options.method)
            ? _options.url + "?" + _options.data
            : _options.url;

        return new Promise(function (res, rej) {
            const xhr = new XMLHttpRequest();
            xhr.open(_options.method, _options.url, _options.async);

            _options.header.authorization &&
                xhr.setRequestHeader(
                    "authorization",
                    _options.header.authorization
                );

            if (_options.data !== "" && /^post$/i.test(_options.method)) {
                xhr.setRequestHeader(
                    "content-type",
                    _options.header["content-type"]
                );
            }

            /^post$/i.test(_options.method)
                ? xhr.send(_options.data)
                : xhr.send();

            xhr.onload = function () {
                if (_options.dataType === "string") {
                    return res({
                        code: 1,
                        msg: "请求成功",
                        info: xhr.responseText,
                    });
                }

                // 代码运行到这个位置说明, 我们需要转换一下数据格式

                try {
                    const newData = JSON.parse(xhr.responseText);
                    res({
                        code: 1,
                        msg: "请求成功",
                        info: newData,
                    });
                } catch (error) {
                    res({
                        code: 0,
                        msg: "请求失败",
                        info: error,
                    });
                }
            };
        });
    };
}
export const myAjax = newFn(baseUrl);