Ajax如何理解

什么是ajax

●认识前后端交互
    ○就是 前端 与 后端的 一种通讯方式
    ○主要使用的技术栈就是 ajax (async javascript and xml)
●ajax 特点
    ○使用 ajax 技术网页应用能够快速的将新内容呈现在用户界面
    ○并且不需要刷新整个页面, 也就是能够让页面有 "无刷更新" 的效果
●注意点:
    ○前后端交互只能交互 字符串
    ○并且有自己的固定步骤
创建ajax 基本步骤的是什么
1.创建 ajax 对象

const xhr = new XMLHttpRequest()

2.配置 ajax 对象

// xhr.open('请求的方式', '请求的地址', 是否异步)xhr.open('GET', 'http://localhost:8888/test/first', true)

3.发送请求

xhr.send()

4.接收响应​​​​​​​

xhr.onload = function () {    console.log('请求回来了~~~~~~~~')    console.log(xhr.responseText)}

ajax 状态码有哪些

●简单来说其实就是用一个数字表明了当前 ajax 运行到哪一步了
●语法: xhr.readyState
    ○0: 创建 ajax 成功
    ○1: 当前 ajax 配置成功
    ○2: 当前 ajax 发送成功(响应已经回到浏览器了)
    ○3: 表示浏览器当前正在解析本次响应, 但可能还没完成
    ○4: 表示浏览器已经完成解析本次响应, 可以正常使用 responseText 了
●0 和 1, 比较好打印, 2/3/4 这几个我们可以借助一个 事件去打印
    ○readyStatechange 事件
    ○通过事件名其实就可以看出, 当 readyState 发生改变时就会执行​​​​​​​

const xhr = new XMLHttpRequest()console.log(xhr.readyState) // 0
xhr.open('GET', 'http://localhost:8888/test/first', true)console.log(xhr.readyState) // 1
xhr.onreadystatechange = function () {    if (xhr.readyState === 2) console.log(xhr.responseText)     if (xhr.readyState === 3) console.log(xhr.responseText)     if (xhr.readyState === 4) console.log(xhr.responseText) }
xhr.send()

ajax 常见请求方式有哪些

● GET
    ○表示向服务器获取资源
●POST
    ○表示向服务器提交信息,通常用于产生新的数据,比如注册
●PUT
    ○表示希望修改服务器的数据, 通常用于修改某数据
●DELETE
    ○表示希望删除服务器的数据
●OPTIONS
    ○发生在跨域的预检请求中,表示客户端向服务器申请跨域提交
ajax 中 get 和 post 请求携带参数的方式
●GET: 直接拼接在请求路径后, 以 ? 间隔, 使用 key=value 的形式书写, 当有多个参数的时候用 & 连接​​​​​​​

const xhr = new XMLHttpRequest()xhr.open('GET', 'http://localhost:8888/test/third?name=QF666&age=18')xhr.send()xhr.onload = function () {    let res = JSON.parse(xhr.responseText)    console.log(res)}

●POST
    ○在请求体内携带参数(其实就是 send 小括号内部)
    ○并且需要设置请求头内部的 content-type
        ■如果参数为 查询字符串, 需要添加:
            ●'xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')'
        ■如果参数为 JSON 字符串, 需要添加:
            ●'xhr.setRequestHeader('content-type', 'application/json')'​​​​​​​

const xhr = new XMLHttpRequest()xhr.open('POST', 'http://localhost:8888/test/fourth')xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')xhr.send('name=QF666&age=18')xhr.onload = function () {    let res = JSON.parse(xhr.responseText)    console.log(res)}

ajax 中 get 和 post 的区别有哪些

    1.携带参数的位置
        a.GET: 直接在地址后面书写
        b.POST: 在请求体内书写
    2.携带参数的大小
        a.GET: 2kb(左右)
        b.POST: 原则上不限制大小, 但是服务器可以做出限制
    3.携带参数的格式
        a.GET: 只能携带查询字符串格式
        b.POST: 原则上不限制格式, 但是需要在请求报文的 content-type 做出配置
    4.安全性(相对)
        a.GET: 明文发送, 相对不安全
        b.POST: 暗文发送, 相对安全

●http 传输协议

    ○http(s) 协议规定了, 只能由前端主动发起
    ○并且在传输的过程中, 只能传递 字符串

●http 协议过程

    1.建立连接

        浏览器和服务器进行连接建立
        基于 TCP/IP 协议的三次握手

    2.发送请求

        要求前端必须以 请求报文 的形式发送

        报文由浏览器组装, 我们只需要提供对应的信息即可

        报文包含的内容

             请求报文行

             请求方式, 请求地址, 传输协议

             请求报文头(对本次请求的一些说明信息)

             userAgent: 请求方终端信息

             accept: 期望后端返回的数据类型

             content-type: 请求携带的 "材料" 的数据格式

             cookie: 只要 cookie 空间内有内容, 会自动携带

        请求报文空行

        请求报文体(不是所有请求都有)

    3.  接收响应

        要求后端必须以响应报文的形式返回

        报文由服务器组装

        响应报文包含的内容

                响应报文行

                响应状态码, 简单信息描述响应状态码, 传输协议

         响应报文头(对本次响应的一些说明信息)

                server: 哪一个服务器给你返回的信息

                date: 时间, 服务器时间

                content-length: 响应体长度

                content-type: 响应数据类型

                响应报文体(后端返回给前端的一些信息)

    4.  断开连接

          浏览器和服务器断开连接

         基于 TCP/IP 协议的四次挥手

● 响应状态码

    ○ 100~199 表示连接继续

    ○ 200~299 表示各种成功

    ○ 300~399 表示重定向

    ○ 400~499 表示各种客户端错误

    ○ 500~599 表示各种服务端错误

● 回调函数

    ○ 把函数 A 以实参的形式传递到 函数 B 内

    ○ 在函数 B 内以形参的方式调用到 函数 A

    ○ 此时我们可以把函数 A 叫做函数 B 的 回调函数

    ○ 我们在封装异步代码的时候会用到回调函数​​​​​​​

function fnA () {console.log('我是 fnA 函数内部的代码')}
function fnB(callback) {callback()}
fnB(fnA)

● 使用回调函数封装一个异步代码​​​​​​​

function fn(jinnang = () => {}) {    console.log("班长去买水了");    const timer = Math.ceil(Math.random() * 3000);    setTimeout(() => {        console.log("班长买完水了");        console.log("耗时", timer);        console.log("按照锦囊内的内容行事");        jinnang();    }, timer);}/** * fn 函数一旦调用, 班长出发开始去买水 *      在班长出发的时候, 给他一个锦囊 */fn(() => {    console.log("去买一瓶牛奶");});fn();

● 上述代码已经完成了一个异步的封装

● 不过在工作中我们更多的是封装网络请求这种异步代码

● 但是我们这里通过一个 '买水耗时' 来模拟一个网络请求的延迟, 我们约定如果时间超过 3500 毫秒, 那么就算是失败, 否则就是成功​​​​​​​

function fn(jinnang = () => {}) {    console.log("班长去买水了");    const timer = Math.ceil(Math.random() * 3000) + 2000;    setTimeout(() => {        if (timer > 3500) {            console.log("请求失败", timer);        } else {            console.log("请求成功", timer);        }    }, timer);}/** * fn 函数一旦调用, 班长出发开始去买水 *      在班长出发的时候, 给他一个锦囊 */fn(() => {    console.log("去买一瓶牛奶");});fn();

● 此时我们已经封装完毕了, 只不过这种封装方式会带来另一个问题, 就是回调地狱

● 回调地狱: 当你使用回调函数过多的时候, 会出现的一种代码书写结构

● 需求:

    ○ 再买水成功后, 让班长帮忙退掉

    ○ 在推掉以后, 再次让班长帮忙买水 (此时必须要在前一瓶水购买完成之后再去购买)

    ○ 在第二次买水成功以后, 再次让班长去买水​​​​​​​

fn(    () => {        console.log("班长第一次买水成功, 帮我退掉");        fn(            () => {                console.log("班长第二次买水成功");                fn(                    () => {                        console.log("班长第三次买水成功");                    },                    () => {                        console.log("班长第三次买水失败");                    }                );            },            () => {                console.log("班长第二次买水失败");            }        );    },    () => {        console.log("班长第一次买水失败");    });

●这段代码运行没有任何问题, 但是阅读起来极其不利于理解
    ○原因:
        ■按照回调函数的语法进行封装, 只能通过传递一个函数作为参数来调用
        ■当你使用回调函数过多的时候, 会出现回调地狱的代码结构
    ○解决:
        ■不按照回调函数的语法封装
        ■ES6 推出了一种新的封装异步代码的方式, 叫做 Promise (承诺, 期约)

Promise

是一种异步代码的封装方案
因为换了一种封装方案, 不需要安装回调函数的方式去调用, 需要按照 promise 的形式去调用
注意 promise 不是解决 异步问题的, 而是解决回调地狱问题的

●认识 Promise

    ○promise 的三种状态
        ■持续: pending
        ■成功: fulfilled
        ■失败: rejected

    ○promise 的两种转换
        ■从持续转为成功
        ■从持续转为失败

    ○promise 的基础语法
        ■ES6 内置构造函数

    ○promise 的语法
        ■const p = new Promise(function () {})

    ○promise 对象可以触发的两个方法
        ■p.then(函数); 成功时执行
        ■p.catch(函数); 失败时执行
●promise 封装一个异步函数

​​​​​​​

const p = new Promise(function (resolve, reject) {    // resolve: 是一个形参, 名字自定义, 值是一个函数, 当你调用的时候, 会把当前 promise 的状态转换为 成功    // reject: 是一个形参, 名字自定义, 值是一个函数, 当你调用的时候, 会把当前 promise 的状态转换为 失败    // resolve 和 reject 调用时可以传递一个参数, 这个参数会被传递给对应的 then catch    const timer = Math.ceil(Math.random() * 3000) + 2000;    setTimeout(() => {        if (timer > 3500) {            console.log("买水失败, 耗时 ", timer);            reject("奖励一个bug");        } else {            console.log("买水成功, 耗时: ", timer);            resolve("送你十个bug");        }    }, timer);});
p.then(function (address) {    console.log("班长买水成功咯~~~", address);});p.catch(function (address) {    console.log("班长买水失败咯~~~", address);});

● 封装 promise 为函数​​​​​​​

function fn() {    const p = new Promise(function (resolve, reject) {        const timer = Math.ceil(Math.random() * 3000) + 2000;        setTimeout(() => {            if (timer > 3500) {                reject("班长买水失败");            } else {                resolve("班长买水成功");            }        }, timer);    });    return p;}// 将来在使用的时候 res 得到的是 promise 的实例对象 p
const res = fn();res.then(function (type) {    // 这个函数执行代码 promise 状态为成功状态!!!    console.log("因为", type, "谢谢班长, 我准备了20个bug, 回馈给你");});res.catch(function (type) {    // 这个函数执行代码    console.log("因为", type, "谢谢班长, 我准备了800个bug, 开心死你");});

●promise 的链式调用

​​​​​​​

fn()    .then(function (type) {        // 这个函数执行代码 promise 状态为成功状态!!!        console.log("因为", type, "谢谢班长, 我准备了20个bug, 回馈给你");    })    .catch(function (type) {        // 这个函数执行代码        console.log("因为", type, "谢谢班长, 我准备了800个bug, 开心死你");    });

●promise 的调用方式补充

    ○如果你在第一个 then 里面返回(return) 一个新的 promise 对象的时候
    ○可以在第一个 then 后面, 继续第二个 then​​​​​​​

fn()    .then(function (type) {        console.log(            "第一次: 因为",            type,            "谢谢班长, 我准备了20个bug, 回馈给你"        );        return fn();    })    .then(function (type) {        console.log(            "第二次: 因为",            type,            "谢谢班长, 我准备了20个bug, 回馈给你"        );        return fn();    })    .then(function (type) {        console.log(            "第三次: 因为",            type,            "谢谢班长, 我准备了20个bug, 回馈给你"        );        return fn();    })    .catch(function (type) {        console.log("因为", type, "谢谢班长, 我准备了800个bug, 开心死你");    });

●promise 的其他方法

●Promise 实例的 finally 方法

    ○不管promise是成功还是失败, 只要 promise 执行结束, 我都会执行​​​​​​​

fn()    .then(function (res) {        console.log("成功");    })    .catch(function (res) {        console.log("失败");    })    .finally(function () {        console.log(            "不管promise是成功还是失败, 只要 promise 执行结束, 我都会执行"        );    });

● Promise 本身还有一些方法

    ○all:
        ■作用: 可以同时触发多个 promise 行为
            ●只有所有的 promise 都成功的时候, all 才算成功
            ●只要任何一个 promise 失败的时候, all 就算失败了
        ■语法: Promise.all([多个 promise])

○race:
        ■作用: 可以同时触发多个 promise 行为
            ●按照速度计算, 当第一个结束的时候就结束了, 成功或失败取决于第一个执行结束的 promise
        ■语法: Promise.race([多个 promise])

○allSettled
        ■作用: 可以同时触发多个 Promise 行为
            ●不管多个成功还是失败都会触发
            ●会在结果内以数组的形式给你返回 每一个 promise 行为的成功还是失败
        ■语法: Promise.allSettled([多个 promise])

○resolve
        ■作用: 强制返回一个成功状态的 promise 对象

○reject
        ■作用: 强制返回一个失败状态的 promise 对象

​​​​​​​

// 1. allPromise.all([fn(), fn(), fn()])    .then(function () {        console.log("所有的 参数 都返回 成功状态");    })    .catch(function () {        console.log("这些参数中, 有一个 为 失败状态");    });// 2. racePromise.race([fn(), fn(), fn()])    .then(function () {        console.log("速度最快的那个执行完毕, 并且是成功状态时 执行");    })    .catch(function () {        console.log("速度最快的那个执行完毕, 并且是失败状态时 执行");    });// 3. allSettledPromise.allSettled([fn(), fn(), fn()])    .then(function (res) {        console.log(res);    })    .catch(function (res) {        console.log(res);    });// 4. resolvePromise.resolve()    .then(function (res) {        console.log("成功");    })    .catch(function (res) {        console.log("失败");    });// 5. rejectPromise.reject()    .then(function (res) {        console.log("成功");    })    .catch(function (res) {        console.log("失败");    });

●async / await

上述我们已经把 promise 的基础使用掌握了, 但是个人认为, promise 的链式调用仍然会有点小问题
就是在使用的时候, 过多的链式调用, 对于阅读体验来说, 仍然是有一点小问题, 不利于阅读
所以我们可以 使用 ES6+ 新推出的 async与await, 使用我的异步代码书写的更像是同步代码一样

●注意: 需要配合的必须是 Promise
●async 关键字的用法:
    ○直接书写在函数的前面即可, 表示该函数是一个异步函数
    ○意义: 表示在该函数内部可以使用 await 关键字
●await 关键字的用法:
    ○必须书写在一个有 async 关键字的函数内
    ○await 后面等待的内容必须是一个 promise 对象
    ○本该使用 then 接受的结果, 可以直接定义变量接受了
●常规的 promise 调用方式

​​​​​​​

fn()    .then(function (res) {        console.log(res);    })    .catch(function (res) {        console.log(res);    });

●利用 async 和 await 关键字来使用

​​​​​​​

async function newFn() {    /**     * await 是等待的意思     *     *  在当前 fn 函数内, await 必须要等到后面的 Promise 结束以后, 才会继续执行后续代码     */    const r1 = await fn();    console.log("第一次: ", r1);    const r2 = await fn();    console.log("第二次: ", r1);    const r3 = await fn();    console.log("第三次: ", r1);}
newFn();

●async 和 await 语法的缺点
    ○await 只能捕获到 Promise 成功的状态
    ○如果失败, 会报错并且终止程序的继续执行​​​​​​​

async function newFu() {    const r1 = await fn();    console.log("失败后, 提示用户网络错误"); // 如果失败的话这行代码并不会执行}newFu();

● 解决方法1: 使用 try...catch...
    ○语法: try { 执行代码 } catch (err) { 执行的代码 }
    ○首先执行 try 内部的代码, 如果不报错, catch 的代码不执行了
    ○如果报错, 不会爆出错误, 不会终止程序, 而是执行 catch 的代码, 报错信息在 catch 函数的形参内

​​​​​​​

async function newFu() {    try {        const r1 = await fn();        console.log(r1);    } catch (error) {        console.log("网络错误, 请检查网络并重新请求");    }}newFu();

●解决方法2: 改变封装的思路
    ○原因: 因为 promise 对象有成功和失败的状态, 所以会在失败状态是报错
    ○解决: 封装一个 百分比成功的 promise 对象, 让成功和失败的时候都按照 resolve 的形式来执行
    ○只不过传递出去的参数, 记录一个表示成功或者失败的信息

​​​​​​​

function fn() {    const p = new Promise(function (resolve, reject) {        const timer = Math.ceil(Math.random() * 3000) + 2000;        setTimeout(() => {            if (timer > 3500) {                resolve({ code: 0, msg: "班长买水失败" });            } else {                resolve({ code: 1, msg: "班长买水成功" });            }        }, timer);    });    return p;}
async function newFn() {    const r1 = await fn();    if (r1.code === 0) {        console.log("第一次请求失败, 请检查您的网络信息");    } else {        console.log("第一次请求成功", r1.msg);    }
    const r2 = await fn();    if (r2.code == 0) {        console.log("第二次请求失败, 请检查您的网络信息");    } else {        console.log("第二次请求成功", r2.msg);    }}newFn();

●封装 ajax

1.ajax 封装解析

    a.封装方案

        i.选择回调函数

            1.将来使用的时候需要按照回调函数的语法使用
            2.但是容易出现回调地狱

        ii.选择 promise 的形式

            1.按照 Promise 的形式来使用
            2.后续可以利用 async/await 语法进一步简化代码

    b.决定参数

        i. 请求地址(url): 必填
        ii.请求方式(method): 选填, 默认 GET
        iii.是否异步(async): 选填, 默认 true 异步
        iv.携带的参数(data): 选填, 默认 '' 空字符

    c.决定返回值

        i.需要
        ii.返回一个 promise 对象
            1.因为需要依赖返回值来决定使用 then 还是 catch
    d.参数书写顺序
        i.因为有多个参数, 并且有些参数可以有默认值
        ii.所以我们只能通过传递一个对象的方式去处理

function ajax(options) {}

2.ajax 封装参数验证​​​​​​​         

function ajax(options = {}) {    // 1.1 验证参数---url 必传    if (options.url == undefined) throw new Error("url 为必填项, 请填写后再试");
    // 1.2 验证参数---method 可以不传, 可以为 GET, 可以是 POST, 但不能是其他的    if (        !(options.method === undefined || /^(GET|POST)$/i.test(options.method))    ) {        throw new Error("mehtod 目前仅支持 GET 或 POST");    }    // 1.3 验证参数---async 可以不传, 可以为 true, 可以是 false, 但不能是其他的    if (!(options.async === undefined || typeof options.async === "boolean")) {        throw new Error("async 目前仅支持 布尔值(Boolean)");    }    // 1.4 验证参数---data 可以不传, 可以为一个字符串, 可以是一个对象    if (        !(            options.data === undefined ||            typeof options.data === "string" ||            options.data.constructor === Object        )    ) {        throw new Error("data 目前仅支持 字符串(String) 或 对象(Object)");    }
    // console.log('这里执行说明参数的 url 已经传递了')}
ajax({    url: "qwer",    // method: 'POST',    // async: false,    data: "name=QF666&age=18",});

3.ajax 封装默认值​​​​​​​

function objToStr(obj) {    let str = "";
    for (let k in obj) {        str += `${k}=${obj[k]}&`;    }
    return str.slice(0, str.length - 1);}
function ajax(options = {}) {    // 1 验证参数---代码省略
    // 2.1 处理默认值    const _options = {        url: options.url,        // 代码执行到这里, method 要么是 undefined 要么是 GET或者POST        method: options.method || "GET",        // 代码执行到这里, async 要么是 undefined 要么是 true 或者 false        // ?? 空值运算符, 只有在 左侧为 undefined 或者 null 时才会执行右边的        async: options.async ?? true,        // 代码执行到这里, data 要么是 undefined 要么是 '' 或者 {}        data: options.data || "",    };    // 2.2 如果当前 data 是对象, 需要转换为 查询字符串    if (typeof _options.data !== "string") {        _options.data = objToStr(_options.data);    }    // 2.3 如果当前 data 有值, 且当前是 GET 方式, 那么可以提前 把 url 后拼接上 data    if (_options.data && /^(GET)$/i.test(_options.method)) {        _options.url += "?" + _options.data;    }
    console.log("原始参数: ", options);    console.log("默认参数: ", _options);
    // console.log('这里执行说明参数的 url 已经传递了')}

4.ajax 封装发送请求​​​​​​​

function ajax(options = {}) {    // 1 验证参数---代码省略    // 2 处理默认参数---代码省略        // 3. 封装 ajax 请求    const p = new Promise((resolve, reject) => {        // 3.1 创建 ajax        const xhr = new XMLHttpRequest();
        // 3.2 配置 ajax 请求信息        xhr.open(_options.method, _options.url, _options.async);
        // 3.3 配置接收响应的事件        xhr.onload = function () {            resolve(xhr.responseText);        };
        // 3.4 发送本次请求        xhr.send();    });    return p;}
ajax({    url: "http://localhost:8888/test/third",    // method: "POST",    async: false,    data: "name=QF666&age=18",    // data: {    //     name: "QF666",    //     age: 18,    //     abc: 123,    // },}).then((res) => {    console.log(res);});

5.ajax 封装请求头信息​​​​​​​

function ajax(options = {}) {    // 1 验证参数---部分代码省略    // 1.5 验证参数---headers 可以不传, 可以为一个对象    if (        !(            options.headers === undefined ||            options.headers.constructor === Object        )    ) {        throw new Error("herder 目前仅支持对象(Object)");    }
    // 2.1 处理默认值    const _options = {        // code...        // 代码执行到这里, headers 要么是 undefined 要么是 {}        headers: {            "content-type": "application/x-www-form-urlencoded",            ...options.headers,        },    };
    // 3. 封装 ajax 请求    const p = new Promise((resolve, reject) => {        // 3.1 创建 ajax        // 3.2 配置 ajax 请求信息        // 3.3 配置接收响应的事件
        // 如果当前的请求方式为 POST, 那么需要配置上对应的 请求头        if (/^(POST)$/i.test(_options.method)) {            xhr.setRequestHeader(                "content-type",                _options.headers["content-type"]            );        }        // 如果 authorization 内有值, 需要带上 authorization        if (_options.headers.authorization) {            xhr.setRequestHeader(                "authorization",                _options.headers.authorization            );        }        // 3.4 发送本次请求        /^(POST)$/i.test(_options.method)            ? xhr.send(_options.data)            : xhr.send();    });    return p;}
/** *  思考: 请求头的设置 *      + 当你是 post 的时候, 需要设置 content-type 字段 *      + 有的时候还需要设置 authorization 字段 *      + 有的时候还需要设置 abcd 字段 * *  例子: *      1. 登录 *          + POST 方式 *              => 需要 content-type *              => 不需要 authorization *      2. 获取商品列表 *          + GET 方式 *              => 不需要 content-type *              => 不需要 authorization *      3. 获取购物车列表 *          + GET 方式 *              => 不需要设置 content-type *              => 需要 authorization *      4. 修改密码 *          + POST 方式 *              => 需要 content-type *              => 需要 authorization */
ajax({    url: "http://localhost:8888/test/third",    // method: "POST",    // async: false,    // data: "name=QF666&age=18",    data: {        name: "QF666",        age: 18,        abc: 123,    },    headers: { authorization: "123" },}).then((res) => {    console.log(res);});

6.ajax 封装解析参数​​​​​​​

function ajax(options = {}) {    // 1.6 验证参数---dataType 可以不传, 可以为 'string' 可以为 'json'    if (        !(            options.dataType === undefined ||            /^(string|json)$/i.test(options.dataType)        )    ) {        throw new Error("dataType 目前仅支持 'string' 或者 'json' ");    }
    // 2.1 处理默认值    const _options = {        // 代码执行到这里, dataType 要么是 undefined 要么是 string 要么是 json        dataType: options.dataType || "string",    };
    // 3. 封装 ajax 请求    const p = new Promise((resolve, reject) => {        // 3.3 配置接收响应的事件        xhr.onload = function () {            if (_options.dataType === "string") {                resolve({                    code: 1,                    info: xhr.responseText,                    msg: "成功",                });                return;            }
            try {                const res = JSON.parse(xhr.responseText);                resolve({                    code: 1,                    info: res,                    msg: "成功",                });            } catch (error) {                resolve({                    code: 0,                    info: error,                    msg: "失败",                });            }        };    });    return p;}

7.ajax 封装基准地址​​​​​​​

function outer(url) {    let baseUrl = url;
    function ajax(options = {}) {        // 2.1 处理默认值        const _options = {            url: baseUrl + options.url,        };                // 省略部分代码...    }    return ajax;}
const res = outer("http://localhost:8888");
res({    url: "/test/first",}).then((res) => {    console.log(res);});
res({    url: "/test/second",    dataType: "json",}).then((res) => {    console.log(res);});

8.完整版 ajax 封装代码​​​​​​​

function objToStr(obj) {    let str = "";
    for (let k in obj) {        str += `${k}=${obj[k]}&`;    }
    return str.slice(0, str.length - 1);}
function outer(url) {    let baseUrl = url;
    function ajax(options = {}) {        // 1.1 验证参数---url 必传        if (options.url == undefined)            throw new Error("url 为必填项, 请填写后再试");
        // 1.2 验证参数---method 可以不传, 可以为 GET, 可以是 POST, 但不能是其他的        if (            !(                options.method === undefined ||                /^(GET|POST)$/i.test(options.method)            )        ) {            throw new Error("mehtod 目前仅支持 GET 或 POST");        }        // 1.3 验证参数---async 可以不传, 可以为 true, 可以是 false, 但不能是其他的        if (            !(options.async === undefined || typeof options.async === "boolean")        ) {            throw new Error("async 目前仅支持 布尔值(Boolean)");        }        // 1.4 验证参数---data 可以不传, 可以为一个字符串, 可以是一个对象        if (            !(                options.data === undefined ||                typeof options.data === "string" ||                options.data.constructor === Object            )        ) {            throw new Error("data 目前仅支持 字符串(String) 或 对象(Object)");        }        // 1.5 验证参数---headers 可以不传, 可以为一个对象        if (            !(                options.headers === undefined ||                options.headers.constructor === Object            )        ) {            throw new Error("herder 目前仅支持对象(Object)");        }        // 1.6 验证参数---dataType 可以不传, 可以为 'string' 可以为 'json'        if (            !(                options.dataType === undefined ||                /^(string|json)$/i.test(options.dataType)            )        ) {            throw new Error("dataType 目前仅支持 'string' 或者 'json' ");        }
        // 2.1 处理默认值        const _options = {            url: baseUrl + options.url,            // 代码执行到这里, method 要么是 undefined 要么是 GET或者POST            method: options.method || "GET",            // 代码执行到这里, async 要么是 undefined 要么是 true 或者 false            // ?? 空值运算符, 只有在 左侧为 undefined 或者 null 时才会执行右边的            async: options.async ?? true,            // 代码执行到这里, data 要么是 undefined 要么是 '' 或者 {}            data: options.data || "",            // 代码执行到这里, headers 要么是 undefined 要么是 {}            headers: {                "content-type": "application/x-www-form-urlencoded",                ...options.headers,            },            // 代码执行到这里, dataType 要么是 undefined 要么是 string 要么是 json            dataType: options.dataType || "string",        };        // 2.2 如果当前 data 是对象, 需要转换为 查询字符串        if (typeof _options.data !== "string") {            _options.data = objToStr(_options.data);        }        // 2.3 如果当前 data 有值, 且当前是 GET 方式, 那么可以提前 把 url 后拼接上 data        if (_options.data && /^(GET)$/i.test(_options.method)) {            _options.url += "?" + _options.data;        }
        // 3. 封装 ajax 请求        const p = new Promise((resolve, reject) => {            // 3.1 创建 ajax            const xhr = new XMLHttpRequest();
            // 3.2 配置 ajax 请求信息            xhr.open(_options.method, _options.url, _options.async);
            // 3.3 配置接收响应的事件            xhr.onload = function () {                if (_options.dataType === "string") {                    resolve({                        code: 1,                        info: xhr.responseText,                        msg: "成功",                    });                    return;                }
                try {                    const res = JSON.parse(xhr.responseText);                    resolve({                        code: 1,                        info: res,                        msg: "成功",                    });                } catch (error) {                    resolve({                        code: 0,                        info: error,                        msg: "失败",                    });                }            };
            // 如果当前的请求方式为 POST, 那么需要配置上对应的 请求头            if (/^(POST)$/i.test(_options.method)) {                xhr.setRequestHeader(                    "content-type",                    _options.headers["content-type"]                );            }            // 如果 authorization 内有值, 需要带上 authorization            if (_options.headers.authorization) {                xhr.setRequestHeader(                    "authorization",                    _options.headers.authorization                );            }            // 3.4 发送本次请求            /^(POST)$/i.test(_options.method)                ? xhr.send(_options.data)                : xhr.send();        });        return p;    }
    return ajax;}
const res = outer("http://localhost:8888");
res({    url: "/test/first",}).then(res => {    console.log(res)});
res({    url: "/test/second",    dataType: 'json'}).then(res => {    console.log(res)});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/38932.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java技术整理(5)—— Spring篇

Spring是一个全面的全面的、企业应用开发一站式的解决方案,贯穿表现层、业务层、持久层。但是 Spring 仍然可以和其他的框架无缝整合。 1、Spring的核心组件 (1)数据层: JDBC、ORM、OXM、JMS、Transations (2&#x…

Flutter 中,ListView 中需要放置 ListView 需要怎么处理才高效?

问题及场景 ListView 是 Flutter 开发者第一个学习到的 Widget,因为它可以滑动。一切都会运行得很好,直到 ListView 中的 Item 本身也是一个 ListView。你可能会看到 Flutter 建议你将内部的 ListView 的ShrinkWrap 属性设置为 True。虽然错误消除了&am…

连续两年增收不增利,比亚迪电子靠新能源汽车业务再次起飞?

在净利润连续两年下挫之后,比亚迪电子(00285.HK)终于迎来了好消息。 不久前比亚迪电子发布2023年中期盈利预告显示,上半年净利润同比增加115%-146%(2022年上半年的净利润显示6.34亿元)。 这主要受益于大客…

包管理工具 nvm npm nrm yarn cnpm npx pnpm详解

包管理工具 nvm npm yarn cnpm npx pnpm npm、cnpm、yarn、pnpm、npx、nvm的区别:https://blog.csdn.net/weixin_53791978/article/details/122533843 npm、cnpm、yarn、pnpm、npx、nvm的区别:https://blog.csdn.net/weixin_53791978/article/details/1…

【Freertos基础入门】2个Freertos的Delay函数

文章目录 前言一、vTaskDelay与vTaskDelayUntil二、示例代码总结 前言 本系列基于stm32系列单片机来使用freerots 任务管理是实时操作系统(RTOS)的核心功能之一,它允许开发者以并发的方式组织和管理多个任务。FreeRTOS 是一个流行的开源RTO…

嵌入式开发中常用且杂散的命令

1、mount命令 # 挂载linux系统 mkdir /tmp/share mount -t nfs 10.77.66.88:/share/ /tmp/share -o nolock,tcp cd /tmp/share# 挂载Windows系统 mkdir /tmp/windows mount -t nfs 10.66.77.88:/c/public /tmp/windows -o nolock,tcp cd /tmp/windows# 挂载vfat格式的U盘 mkdi…

强训第32

选择 D B A A 发送TCP意思应该是已经建立了连接,会超时重传。在未建立连接的时候,会放弃该链接 C A 80端口是http A 交换机攻击主要有五种:VLAN跳跃攻击 生成树攻击 MAC表洪水攻击 ARP攻击 VTP攻击 B A 2^(32-26)2^(32-27)2^(32-27)128 减去…

基于Java+SpringBoot+Vue+echarts健身房管理系统设计和实现

博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专…

maven Jar包反向install到本地仓库

maven Jar包反向install到本地仓库 需求实现 需求 项目打包时报错,缺少一个jar包。 但是在maven仓库都找不到此jar包,其他人提供了这个jar包。 需要把这个jar包install到本地仓库,使项目能正常打包运行。 实现 使用git bash命令执行以下脚…

16.3.4 【Linux】系统资源的观察

free :观察内存使用情况 系统当中有 2848MB 左右的实体内存,我的 swap 有 1GB 左右, 那我使用free -m 以 MBytes 来显示时,就会出现上面的信息。Mem 那一行显示的是实体内存的量,Swap 则是内存交换空间的量。 total 是…

C++多态

文章目录 🐵1. 什么是多态🐶2. 构成多态的条件🐩2.1 虚函数🐩2.2 虚函数的重写🐩2.3 final 和 override关键字🐩2.4 重载、重写、重定义对比 🐱3. 虚函数表🐯4. 多态的原理&#x1f…

神经网络基础-神经网络补充概念-17-计算神经网络的输出

计算神经网络的输出通常涉及前向传播(Forward Propagation)的过程,其中输入数据通过网络的层级结构,逐步被传递并变换,最终生成预测结果。下面我将为你展示一个简单的神经网络前向传播的示例。 假设我们有一个具有以下…

【变形金刚01】attention和transformer所有信息

图1.来源:Arseny Togulev在Unsplash上的照片 一、说明 这是一篇 长文 ,几乎讨论了人们需要了解的有关注意力机制的所有信息,包括自我注意、查询、键、值、多头注意力、屏蔽多头注意力和转换器,包括有关 BERT 和 GPT 的一些细节。因…

OpenCV图像处理——轮廓检测

目录 图像的轮廓查找轮廓绘制轮廓 轮廓的特征轮廓面积轮廓周长轮廓近似凸包边界矩形最小外接圆椭圆拟合直线拟合 图像的矩特征矩的概念图像中的矩特征 图像的轮廓 查找轮廓 binary,contours,hierarchycv.findContours(img,mode,method)绘制轮廓 cv.drawContours(img,coutours…

WSL2安装Ubuntu,配置机器学习环境

文章目录 1.WSL2安装Ubuntu,更改安装位置,作为开发环境供vscode和pycharm使用:2.更换国内源:3.安装图形界面:4.安装cudacudnntorch5.安装opencv6.调用摄像头7.使用yolov8测试 WSL全称Windows Subsystem for Linux&…

印度货代专线【我国到印度专线有哪些方式】

随着全球贸易的不断发展,我国与印度之间的贸易往来也日益频繁。作为两个人口最多的国家之一,中国和印度之间的货物运输需求不断增长。为了满足这一需求,印度货代专线应运而生,为进出口商提供高效、可靠的货物运输服务。本文将探索…

939. 最小面积矩形;2166. 设计位集;2400. 恰好移动 k 步到达某一位置的方法数目

939. 最小面积矩形 核心思想:枚举矩形的右边那条边的两个点,并用一个哈希表存储相同纵坐标的最近出现的列的列数,不断更新最近出现的左边那条边。 2166. 设计位集 核心思想:这题主要是时间复杂度的优化,用一个flag来标记当前翻转…

CSS自学框架之表单

首先我们看一下表单样式,下面共有5张截图 一、CSS代码 /*表单*/fieldset{border: none;margin-bottom: 2em;}fieldset > *{ margin-bottom: 1em }fieldset:last-child{ margin-bottom: 0 }fieldset legend{ margin: 0 0 1em }/* legend标签是CSS中用于定义…

IOS开发-XCode14介绍与入门

IOS开发-XCode14介绍与入门 1. XCODE14的小吐槽2. XCODE的功能bar一览3. XCODE项目配置一览4. XCODE更改DEBUG/RELEASE模式5. XCODE单元测试 1. XCODE14的小吐槽 iOS开发工具一直有个毛病,就是新版本的开发工具的总会有一些奇奇怪怪的bug。比如在我的Mac-Pro&#…

Springboot 实践(3)配置DataSource及创建数据库

前文讲述了利用MyEclipse2019开发工具,创建maven工程、加载springboot、swagger-ui功能。本文讲述创建数据库,为项目配置数据源,实现数据的增删改查服务,并通过swagger-ui界面举例调试服务控制器 创建数据库 项目使用MySQL 8.0.…