[toc]

Ajax技术

一. 初识前后端交互

传统网站的问题:

  • 为了获取数据,需要重新加载,浪费资源,增加等待时间,性能不好
  • 验证表单过程中,一项内容不合格,页面需要重新加载,体验不好
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, user-scalable=no, initial-scale=1.0"
    />
    <title>01_ajax之前的前后端交互</title>
  </head>
  <body>
    <!-- 表单提交后跳到http://www.baidu.com?username=xxx&password=xxx -->
    <form action="http://www.baidu.com">
      <p>用户名<input type="text" name="username" /></p>
      <p>密码 <input type="password" name="password" /></p>
      <button type="submit">登录</button>
    </form>
  </body>
</html>

**解决方案:**

  • ajax 全名 async javascript and XML
  • 是前后台交互的能力
  • 也就是我们客户端给服务端发送消息的工具,以及接受响应的工具
  • 是一个 默认异步 执行机制的功能(异步,就是不阻塞,不一定要等到请求返回响应,而是继续执行,等js空闲的时候,再执行回调函数)

AJAX 的优势

  1. 不需要插件的支持,原生JavaScript就可以使用(ajax是一种异步前后端技术的概念, 而XMLHttpRequest才是javascript里的内容)
  2. 用户体验好(不需要刷新页面就可以更新数据)
  3. 减轻服务端和带宽的负担
  4. 缺点: 搜索引擎的支持度不够,因为数据都不在页面上,搜索引擎搜索不到

二. 原生Ajax

1. AJAX 基础

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

创建一个 ajax 对象

// IE9及以上
const xhr = new XMLHttpRequest()

// IE9以下
const xhr = new ActiveXObject('Mricosoft.XMLHTTP')
  • 上面就是有了一个 ajax 对象
  • 我们就可以使用这个 xhr 对象来发送 ajax 请求了

配置链接信息

const xhr = new XMLHttpRequest()

// xhr 对象中的 open 方法是来配置请求信息的
// 第一个参数是本次请求的请求方式 get / post / put / ...
// 第二个参数是本次请求的 url 
// 第三个参数是本次请求是否异步,默认 true 表示异步,false 表示同步
// xhr.open('请求方式', '请求地址', 是否异步)
xhr.open('get', './data.php')
  • 上面的代码执行完毕以后,本次请求的基本配置信息就写完了

发送请求

const xhr = new XMLHttpRequest()
xhr.open('get', './data.php')

// 使用 xhr 对象中的 send 方法来发送请求
xhr.send()
  • 上面代码是把配置好信息的 ajax 对象发送到服务端

一个基本的 ajax 请求

  • 一个最基本的 ajax 请求就是上面三步
  • 但是光有上面的三个步骤,我们确实能把请求发送的到服务端
  • 如果服务端正常的话,响应也能回到客户端
  • 但是我们拿不到响应
  • 如果想拿到响应,我们有两个前提条件
    1. 本次 HTTP 请求是成功的,也就是我们之前说的==http 状态码为 200 ~ 299==
    2. ajax 对象也有自己的状态码,用来表示本次 ajax 请求中各个阶段

ajax 状态码

  • ajax 状态码 - xhr.readyState
  • 是用来表示一个 ajax 请求的全部过程中的某一个状态
    • readyState === 0: 表示未初始化完成,也就是 open 方法还没有执行
    • readyState === 1: 表示配置信息已经完成,也就是执行完 open 之后
    • readyState === 2: 表示 send 方法已经执行完成
    • readyState === 3: 表示正在解析响应内容
    • readyState === 4: 表示响应内容已经解析完毕,可以在客户端使用了
  • 这个时候我们就会发现,当一个 ajax 请求的全部过程中,只有当 readyState === 4 的时候,我们才可以正常使用服务端给我们的数据
  • 所以,配合 http 状态码为 200 ~ 299
    • 一个 ajax 对象中有一个成员叫做 xhr.status
    • 这个成员就是记录本次请求的 http 状态码的
  • 两个条件都满足的时候,才是本次请求正常完成
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Ajax基础</title>
  </head>
  <body>
    <button>你好</button>
    <script>
      let xhr = new XMLHttpRequest();

      // 参数1: 请求方式, 参数2: 请求地址(可以相对或者绝对路径), 参数3: 是否异步
      xhr.open("GET", "1.txt", true); // get请求,参数放在url中
      let button = document.querySelector("button");
      button.addEventListener("click", function () {
        xhr.send(); // 发送请求
      });

      // 监听状态改变
      xhr.onreadystatechange = function () {
        // 发送请求之后, 状态改变经过多次改变
        // 状态码为4, 说明请求成功
        if (xhr.readyState === 4) {
          //if (xhr.status === 200) { // 状态码200 ~ 299 为正常
          if (/^2\d{2}$/.test(xhr.status)) {
            // 使用正则表达式判断
            console.log(JSON.parse(xhr.responseText));
          } else {
            console.log("请求失败", xhr.responseText);
          }
        }
      };

      // 直接使用xhr.onload
      xhr.onload = function () {
        if (/^2\d{2}$/.test(xhr.status)) {
          console.log(xhr.response);
        } else {
          console.log("请求失败", xhr.response);
        }
      };
    </script>
  </body>
</html>

readyStateChange

  • 在 ajax 对象中有一个事件,叫做 readyStateChange 事件

  • 这个事件是专门用来监听 ajax 对象的 readyState 值改变的的行为

  • 也就是说只要 readyState 的值发生变化了,那么就会触发该事件

  • 所以我们就在这个事件中来监听 ajax 的 readyState 是不是到 4 了

    const xhr = new XMLHttpRequest()
    xhr.open('get', './data.php')
    
    xhr.send()
    
    xhr.onreadyStateChange = function () {
      // 每次 readyState 改变的时候都会触发该事件
      // 我们就在这里判断 readyState 的值是不是到 4
      // 并且 http 的状态码是不是 200 ~ 299
      if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
        // 这里表示验证通过
        // 我们就可以获取服务端给我们响应的内容了
      }
    }

responseText

  • ajax 对象中的 responseText 成员

  • 就是用来记录服务端给我们的响应体内容的

  • 所以我们就用这个成员来获取响应体内容就可以

    const xhr = new XMLHttpRequest()
    xhr.open('get', './data.php')
    
    xhr.send()
    
    xhr.onreadyStateChange = function () {
      if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
        // 我们在这里直接打印 xhr.responseText 来查看服务端给我们返回的内容
        console.log(xhr.responseText)
      }
    }

2. Ajax案例

!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Ajax案例1</title>
  </head>
  <body>
    <button id="btn">click</button>
    <ul></ul>
    <script>
      var btn = document.getElementById("btn");
      btn.addEventListener("click", function () {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "data.json", true);
        xhr.send();
        xhr.onload = function () {
          if (/^2\d{2}$/.test(xhr.status)) {
            var res = JSON.parse(xhr.responseText);
            render(res);
          } else {
            console.log("error");
          }
        };
      });

      function render(res) {
        console.log(res.projects); //"projects": [{},{}]
        var newlist = res.projects.map(function (item) {
          // map()方法返回一个新数组,其中的元素是对原数组每个元素调用函数处理后返回的结果。
          return `<li>
            <img src="${item.imgurl}" alt="${item.name}"/>
            <div>${item.name}</div>
            </li>`; // 返回一个li标签的html字符串
        });
        // console.log(newlist);
        var ul = document.querySelector("ul");
        ul.innerHTML = newlist.join(""); // join()方法用于将数组中的所有元素连接成一个字符串。
      }
    </script>
  </body>
</html>

map方法

map 方法是 JavaScript 中数组的一个内置函数,它用于对数组中的每个元素执行一个指定的函数,并返回一个新数组,该新数组包含了结果。

使用方法

map 方法的基本语法如下:

let newArray = array.map(function(element, index, array) {
    // 返回处理后的元素
});
  • element:当前正在处理的数组元素。
  • index:当前元素的索引(可选)。
  • array:调用 map 方法的原数组(可选)。
示例

下面是一个简单的示例,演示如何使用 map 方法:

let numbers = [1, 2, 3, 4, 5];
let doubled = numbers.map(function(number) {
    return number * 2;
});

console.log(doubled); // 输出: [2, 4, 6, 8, 10]

在这个例子中,我们使用 map 方法将 numbers 数组中的每个元素都乘以 2,并返回一个新数组 doubled,其中包含处理后的结果。

使用箭头函数

你也可以使用箭头函数来简化 map 的写法:

let numbers = [1, 2, 3, 4, 5];
let doubled = numbers.map(number => number * 2);

console.log(doubled); // 输出: [2, 4, 6, 8, 10]

es6之字符串模板

ES6(ECMAScript 2015)引入了模板字符串(Template Literals),这是一个非常方便的字符串处理方式。模板字符串不仅可以包含普通字符串,还可以嵌入表达式,并支持多行字符串。

语法

模板字符串使用反引号``` ``括起来,而不是单引号或双引号。

特性
  1. 多行字符串: 模板字符串可以包含换行符,便于书写多行文本。

    const message = `你好,
    欢迎使用ES6的字符串模板。`;
    console.log(message);
  2. 嵌入表达式: 可以使用 ${} 语法在字符串中嵌入表达式。

    const name = '张三';
    const age = 25;
    const greeting = `我叫 ${name},今年 ${age} 岁。`;
    console.log(greeting); // 输出: 我叫 张三,今年 25 岁。
  3. 多种表达式支持: 支持任何有效的JavaScript表达式,包括函数调用和运算。

    const a = 5;
    const b = 10;
    const sum = `5 + 10 = ${a + b}`;
    console.log(sum); // 输出: 5 + 10 = 15
嵌套模板字符串

可以通过反引号直接在模板字符串中嵌套其他模板字符串:

const name = '李四';
const info = `姓名: ${name},职业: ${`软件工程师}`}`;
console.log(info); // 输出: 姓名: 李四,职业: 软件工程师

3. Ajax参数

使用Node第三方包模拟后端接口

  1. 安装node.js
  2. 安装json-server包(注意要求的node.js版本)
npm install -g json-server
  1. 使用json-server
json-server ./db.json --watch

此命令是将当前路径下的db.json文件的每一项变成一个路由接口,启动在 loaclhost:3000

json{
  "list": [],
  "users": [],
  "shopcar": [],
  "detail": {
    "name": "手机"
  }
}
http://localhost:3000/list
http://localhost:3000/users
http://localhost:3000/shopcar
http://localhost:3000/detail

使用 ajax 发送请求时携带参数

  • 我们使用 ajax 发送请求也是可以携带参数的
  • 参数就是和后台交互的时候给他的一些信息
  • 但是携带参数 get 和 post 两个方式还是有区别的
发送一个带有参数的 get 请求
  • get 请求的参数就直接在 url 后面进行拼接就可以(参数直接暴露)

    const xhr = new XMLHttpRequest()
    // 直接在地址后面加一个 ?,然后以 key=value 的形式传递
    // 两个数据之间以 & 分割
    xhr.open('get', './data.php?a=100&b=200')
    
    xhr.send()
    • 这样服务端就能接受到两个参数
    • 一个是 a,值是 100
    • 一个是 b,值是 200
发送一个带有参数的 post 请求
  • post 请求的参数是携带在请求体中的,所以不需要再 url 后面拼接

    const xhr = new XMLHttpRequest()
    xhr.open('get', './data.php')
    
    // 如果是用 ajax 对象发送 post 请求,必须要先设置一下请求头中的 content-type
    // 告诉一下服务端我给你的是一个什么样子的数据格式
    xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
    
    // 请求体直接再 send 的时候写在 () 里面就行
    // 不需要问号,直接就是 'key=value&key=value' 的形式
    xhr.send('a=100&b=200')
    • application/x-www-form-urlencoded 表示的数据格式就是 key=value&key=value
不同的请求方式
  • get 偏向获取

  • post 偏向提交

  • put 偏向更新(覆盖式的更新)

  • patch 偏向修改部分(补丁式的更新)

  • delete 偏向删除信息

  • head 偏向获取服务器头的信息

  • option 偏向获取服务器设备信息

  • connnect 保留请求方式

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button id="get">get</button>
    <button id="post">post</button>
    <button id="put">put</button>
    <button id="patch">patch</button>
    <button id="delete">delete</button>
    <script>
      var getBtn = document.getElementById("get");
      var postBtn = document.getElementById("post");
      var putBtn = document.getElementById("put");
      var patchBtn = document.getElementById("patch");
      var deleteBtn = document.getElementById("delete");

      getBtn.onclick = function () {
        var xhr = new XMLHttpRequest();
        // xhr.open("GET", "http://localhost:3000/list", true);
        xhr.open("GET", "http://localhost:3000/users?id=3a8b", true);
        xhr.send();
        xhr.onload = function () {
          if (/^2\d{2}$/.test(xhr.status)) {
            var res = JSON.parse(xhr.responseText);
            console.log(res);
          } else {
            console.log("error");
          }
        };
      };
      postBtn.onclick = function () {
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "http://localhost:3000/users", true);
        // form编码 name=value&name=value
        // json {"name": "value", "name": "value"}
        // xhr.setRequestHeader(
        //   "content-type",
        //   "application/x-www-form-urlencoded"
        // );
        // xhr.send(`name=wangwu&age=40`); // 参数放在send方法中

        xhr.setRequestHeader("Content-Type", "application/json");
        xhr.send(JSON.stringify({ name: "wangwu", age: 40 })); // 参数放在send方法中

        xhr.onload = function () {
          if (/^2\d{2}$/.test(xhr.status)) {
            var res = JSON.parse(xhr.responseText);
            console.log(res);
          } else {
            console.log("error", xhr.responseText);
          }
        };
      };
      putBtn.onclick = function () {
        var xhr = new XMLHttpRequest();
        xhr.open("PUT", "http://localhost:3000/users/3a8b", true); // 注意要更新数据的id放在url中
        xhr.setRequestHeader("Content-Type", "application/json");
        // xhr.send(JSON.stringify({ age: 40 })); // 覆盖式更新,不写其他字段将会删除原有数据
        xhr.send(JSON.stringify({ name: "张三", age: 40 }));

        xhr.onload = function () {
          if (/^2\d{2}$/.test(xhr.status)) {
            var res = JSON.parse(xhr.responseText);
            console.log(res);
          } else {
            console.log("error", xhr.responseText);
          }
        };
      };
      patchBtn.onclick = function () {
        var xhr = new XMLHttpRequest();
        xhr.open("PATCH", "http://localhost:3000/users/3a8b", true); // 注意要更新数据的id放在url中
        xhr.setRequestHeader("Content-Type", "application/json");
        xhr.send(JSON.stringify({ age: 100 })); // 增量式更新,只更新指定的字段

        xhr.onload = function () {
          if (/^2\d{2}$/.test(xhr.status)) {
            var res = JSON.parse(xhr.responseText);
            console.log(res);
          } else {
            console.log("error", xhr.responseText);
          }
        };
      };
      deleteBtn.onclick = function () {
        var xhr = new XMLHttpRequest();
        xhr.open("DELETE", "http://localhost:3000/users/3a8b", true); // 注意要删除数据的id放在url中
        xhr.send();

        xhr.onload = function () {
          if (/^2\d{2}$/.test(xhr.status)) {
            var res = JSON.parse(xhr.responseText);
            console.log(res);
          } else {
            console.log("error", xhr.responseText);
          }
        };
      };
    </script>
  </body>
</html>

三. Fetch

XMLHttpRequest 是一个设计粗糙的 API,配置和调用方式非常混乱, 而且基于事件的异步模型写起来不友好。

**兼容性不好 **

对于ie8及以下的浏览器可以使用第三方 polyfill: https://github.com/camsong/fetch-ie8

1. 用法

fetch("http://localhost:3000/users")
            .then(res=>res.json())
            .then(res=>{
                console.log(res)
            })


fetch("http://localhost:3000/users",{
            method:"POST",
            headers:{
                "content-type":"application/json"
            },
            body:JSON.stringify({
                username:"kerwin",
                password:"123"
            })
        })
            .then(res=>res.json())
            .then(res=>{
                console.log(res)
            })

fetch("http://localhost:3000/users/5",{
            method:"PUT",
            headers:{
                "content-type":"application/json"
            },
            body:JSON.stringify({
                username:"kerwin",
                password:"456"
            })
        })
            .then(res=>res.json())
            .then(res=>{
                console.log(res)
            })

fetch("http://localhost:3000/users/5",{
            method:"DELETE"
        })
            .then(res=>res.json())
            .then(res=>{
                console.log(res)
            })

2. 错误处理

//
fetch("http://localhost:3000/users1")
            .then(res=>{
                if(res.ok){
                    return res.json()
                }else{
                    return Promise.reject({
                        status:res.status,
                        statusText:res.statusText
                    })
                }
            })
            .then(res=>{
                console.log(res)
            })
            .catch(err=>{
                console.log(err)
            })

3. 综合案例1

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button id="get">get</button>
    <button id="post">post</button>
    <button id="put">put</button>
    <button id="patch">patch</button>
    <button id="delete">delete</button>
    <script>
      var getBtn = document.getElementById("get");
      var postBtn = document.getElementById("post");
      var putBtn = document.getElementById("put");
      var patchBtn = document.getElementById("patch");
      var deleteBtn = document.getElementById("delete");

      getBtn.onclick = function () {
        // fetch返回promise对象,可以用then方法处理数据,response是fetch返回的response对象
        // fetch("http://localhost:3000/users")
        //   .then((response) => {
        //     //   console.log(response.json());
        //     return response.json(); // 返回promise对象
        //   })
        //   .then((res) => {
        //     console.log(res);
        //   });
        fetch("http://localhost:3000/users?id=95aa")
          .then((res) => res.json())
          .then((res) => console.log(res))
          .catch((err) => console.log(err));
      };
      postBtn.onclick = function () {
        fetch("http://localhost:3000/users", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ name: "李四", age: 30 }),
        })
          .then((res) => res.json())
          .then((res) => console.log(res));
      };
      putBtn.onclick = function () {
        fetch("http://localhost:3000/users/95aa", {
          method: "PUT",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ name: "李四", age: 55 }),
        })
          .then((res) => res.json())
          .then((res) => console.log(res));
      };
      patchBtn.onclick = function () {
        fetch("http://localhost:3000/users/95aa", {
          method: "PATCH",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            age: 20,
          }),
        })
          .then((res) => res.json())
          .then((res) => console.log(res));
      };
      deleteBtn.onclick = function () {
        fetch("http://localhost:3000/users/95aa", {
          method: "DELETE",
        })
          .then((res) => res.json())
          .then((res) => console.log(res));
      };
    </script>
  </body>
</html>

4. 评论案例

json数据

{
  "news": [
    { "id": 1, "title": "男人看了震惊女人看了落泪", "author": "张三" },
    { "id": 2, "title": "震惊, 光天化日之下竟然", "author": "王五" },
    { "id": 3, "title": "速看! 超市大抢购", "author": "李四" }
  ],
  "comments": [
    { "id": 1, "content": "我是男人", "newsID": 1 },
    { "id": 2, "content": "我是女人", "newsID": 1 },
    { "id": 3, "content": "光天化日", "newsID": 2 },
    { "id": 4, "content": "这种事", "newsID": 2 },
    { "id": 5, "content": "我是评论", "newsID": 3 },
    { "id": 6, "content": "我也是评论", "newsID": 4 }
  ]
}

代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Fetch使用案例</title>
  </head>
  <body>
    <input type="text" id="search" />
    <h2 id="title"></h2>
    <ul id="list"></ul>
    <script>
      var osearch = document.querySelector("#search");
      var otitle = document.querySelector("#title");
      var olist = document.querySelector("#list");
      var obtn = document.querySelector("button");
      /*   osearch.oninput = function () {
            var keyword = osearch.value;
            fetch(`http://localhost:3000/news?author=${keyword}`)
              .then((res) => res.json())
              .then((res) => console.log(res));
          }; */
      function throttle(fn, delay) {
        let lastTime = 0;
        return function (...args) {
          const now = Date.now();
          if (now - lastTime >= delay) {
            lastTime = now;
            return fn.apply(this, args);
          }
        };
      }

      //   osearch.oninput = throttle(function () {
      //     var keyword = osearch.value;
      //     fetch(`http://localhost:3000/news?author=${keyword}`)
      //       .then((res) => res.json()) // 获取新闻数据
      //       .then((res) => {
      //         if (res.length > 0) {
      //           otitle.innerHTML = res[0].title; // 显示新闻标题
      //           return fetch(
      //             `http://localhost:3000/comments?newsID=${res[0].id}`
      //           ).then((res) => res.json());
      //         } else {
      //           otitle.innerHTML = "没有找到相关新闻";
      //           // 空数组直接return
      //           return res;
      //         }
      //       })
      //       .then((res) => {
      //         console.log(res); // 打印评论数据
      //         olist.innerHTML = res
      //           .map((item) => `<li>${item.content}</li>`)
      //           .join(""); // 显示评论内容
      //       });
      //   }, 100); // 设置节流的时间间隔为100毫秒

      osearch.oninput = async function () {
        var keyword = osearch.value;
        var res = await fetch(
          `http://localhost:3000/news?author=${keyword}`
        ).then((res) => {
          console.log(res);

          return res.json();
        }); // 获取新闻数据
        var result;
        if (res.length > 0) {
          otitle.innerHTML = res[0].title; // 显示新闻标题
          result = await fetch(
            `http://localhost:3000/comments?newsID=${res[0].id}`
          ).then((res) => {
            // 此处的res是指fetch返回的promise对象
            return res.json();
          }); // 获取评论数据赋值给result
        } else {
          otitle.innerHTML = "没有找到相关新闻";
          result = res;
        }
        olist.innerHTML = result
          .map((item) => `<li>${item.content}</li>`)
          .join("");
        console.log(result);
      };
    </script>
  </body>
</html>

四. Axios

Axios是一个基于promise 的 HTTP 库,可以用在浏览器和 node.js中。

https://www.npmjs.com/package/axios

Axios中文文档 | Axios中文网

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

1. get请求

axios.get("http://localhost:3000/users",{
    params:{
        name:"kerwin"
    }
}).then(res=>{
    console.log(res.data)
})

2. post请求

axios.post("http://localhost:3000/users",{
    name:"kerwin",
    age:100
}).then(res=>{
	console.log(res.data)
})

3. put请求

axios.put("http://localhost:3000/users/12",{
    name:"kerwin111",
    age:200
}).then(res=>{
    console.log(res.data)
})

4. delete请求

axios.delete("http://localhost:3000/users/11").then(res=>{
    console.log(res.data)
})

5. axios(config)配置

axios({
    method: 'post',
    url: 'http://localhost:3000/users',
    data: {
        name: 'kerwin',
        age: 100
    }
})
    .then(res => {
    console.log(res.data)
}).catch(err=>{
    console.log(err)
})

6. axios拦截器

axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    console.log("loading-开始")
    return config;
}, function (error) {
    // Do something with request error
    return Promise.reject(error);
});

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data

    console.log("loading---结束")
    return response;
}, function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    console.log("loading---结束")
    return Promise.reject(error);
});
  • 拦截器放在外面会对整个页面生效
  • axios.interceptors.request.use... 拦截器 表示在axios请求发送之前做一些事
  • axios.interceptors.response.use表示接收到响应,先经过拦截器,在执行then方法
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Axios使用</title>
    <!-- <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script> -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  </head>
  <body>
    <button id="get">get</button>
    <script>
      var getBtn = document.getElementById("get");
      axios.interceptors.request.use(
        function (config) {
          // Do something before request is sent
          console.log("request拦截器---开始");
          return config;
        },
        function (error) {
          // Do something with request error
          return Promise.reject(error);
        }
      );

      // Add a response interceptor
      axios.interceptors.response.use(
        function (response) {
          // Any status code that lie within the range of 2xx cause this function to trigger
          // Do something with response data

          console.log("2xxloading---结束");
          //   return response; // 这里可以对response进行处理,会返回到then的回调函数中的res
          return {
            ...response,
            data: "自定义数据",
          };
        },
        function (error) {
          // Any status codes that falls outside the range of 2xx cause this function to trigger
          // Do something with response error
          console.log("not 2xxloading---结束");
          return Promise.reject(error);
        }
      );

      getBtn.onclick = function () {
        axios.get("http://localhost:3000/news").then((res) => console.log(res));
        //   .then((res) => console.log(res.data));
      };
    </script>
  </body>
</html>

image-20241205223037372

7. axios中断器

const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// cancel the request
controller.abort()
/* 中断器 */
const controller = new AbortController();
getBtn.onclick = function () {
    axios
        .get("http://localhost:3000/news", { signal: controller.signal })
        .then((res) => console.log(res))
        .catch((err) => console.log(err));
};
var abortBtn = document.getElementById("abort");
abortBtn.onclick = function () {
    controller.abort();
};

8. axios请求案例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Axios使用</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
  </head>
  <body>
    <button id="get">get</button>
    <button id="post">post</button>
    <button id="put">put</button>
    <button id="patch">patch</button>
    <button id="delete">delete</button>
    <script>
      var getBtn = document.getElementById("get");
      var postBtn = document.getElementById("post");
      var putBtn = document.getElementById("put");
      var patchBtn = document.getElementById("patch");
      var deleteBtn = document.getElementById("delete");

      getBtn.onclick = function () {
        axios
          // .get("http://localhost:3000/users?name=李四")
          .get("http://localhost:3000/users", {
            params: {
              name: "李四",
            }, // axios可以直接传入参数,不需要自己拼接url
          })
          .then((res) => console.log(res.data))
          .catch((err) => console.log(err));
      };
      postBtn.onclick = function () {
        axios
          // 这样是传入json格式
          // .post("http://localhost:3000/users", {
          //   name: "张三",
          //   age: 20,
          // })
          .post("http://localhost:3000/users", "name=赵六&age=20") // 传入form编码的字符串
          .then((res) => console.log(res.data))
          .catch((err) => console.log(err));
      };
      putBtn.onclick = function () {
        axios
          .put("http://localhost:3000/users/5e39", {
            age: 170, // 完全替换
          })
          .then((res) => console.log(res.data))
          .catch((err) => console.log(err));
      };
      patchBtn.onclick = function () {
        axios
          .patch("http://localhost:3000/users/5e39", {
            age: 170, // 补丁式的更新
          })
          .then((res) => console.log(res.data))
          .catch((err) => console.log(err));
      };
      deleteBtn.onclick = function () {
        axios
          .delete("http://localhost:3000/users/5e39")
          .then((res) => console.log(res.data))
          .then((err) => console.log(err));
      };
    </script>
  </body>
</html>

五. 同源策略(Same-origin policy)

一个 URL 有三部分组成:协议、域名(指向主机)、端口,只有这三个完全相同的 URL 才能称之为同源。如下,能和 http://www.example.com/dir1/index.html 同源的是?

URL 结果 原因
http://www.example.com/dir2/api 同源 只有路径不同
https://www.example.com/api 不同源 协议不同
http://www.example.com:81/dir1/etc.html 不同源 端口不同 ( http:// 默认端口是80)
http://www.kerwin.com/dir1/other.html 不同源 域名不同

(1) 无法读取非同源网页的 Cookie、LocalStorage 。
(2) 无法接触非同源网页的 DOM。
(3) 无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)。

注意:

同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。

六. jsonp

Jsonp(JSON with Padding) 是 json 的一种”使用模式”,可以让网页从别的域名(网站)那获取资料,即跨域读取数据。

为什么我们从不同的域(网站)访问数据需要一个特殊的技术( JSONP )呢?这是因为同源策略

const script = document.createElement('script')
script.src = './kerwin.txt'
document.body.appendChild(script)

实战

mysearch.oninput = function(evt){
    console.log(evt.target.value)
    if(!evt.target.value){
        list.innerHTML = ""
        return 
    }

    var oscript = document.createElement("script")
    oscript.src = `https://www.baidu.com/sugrec?pre=1&p=3&ie=utf-8&json=1&prod=pc&from=pc_web&sugsid=36542,36464,36673,36454,31660,36692,36166,36695,36697,36570,36074,36655,36345,26350,36469,36314&wd=${evt.target.value}&req=2&csor=1&cb=test&_=1656294200527`
    document.body.appendChild(oscript)

    oscript.onload = function(){
        oscript.remove()
    }
}

function test(obj){
    console.log(obj.g)

    list.innerHTML = obj.g.map(item=>
                               `<li>${item.q}</li>`
                              ).join("")
}
  • 还有一种解决跨域访问的手段就是后端在返回HttpResponse响应的时候,设置响应头中Access-Control-Allow-Origin: *

image-20241206133053112

  • 有时候还要在请求头中加上对应的标头
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>卖座网解决跨域</title>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  </head>
  <body>
    <script>
      url =
        "https://m.maizuo.com/gateway?cityId=440300&pageNum=1&pageSize=10&type=1&k=1032295";
      //   axios
      //     .get(url)
      //     .then((res) => console.log(res))
      //     .catch((err) => console.log(err));

      axios({
        url: url,
        headers: {
          "x-client-info":
            '{ "a": "3000", "ch": "1002", "v": "5.2.1", "e": "1733463319789110046326785"}',
          "x-host": "mall.film-ticket.film.list",
        },
      }).then((res) => console.log(res.data.data));
    </script>
  </body>
</html>

image-20241206134902578

七.反向代理

猫眼数据接口https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E5%B9%BF%E5%B7%9E&ci=20&channelId=4

对于这样的接口,既不是jsonp格式,又没有使用Access-Control-Allow-Origin: *

可以使用反向代理

  • 下载nginx
  • 解压到全英文路径下
  • 在exe文件路径下创建一个新文件夹mystatic,并放入你的静态html文件
  • 复制一份./conf/nginx.conf文件,重命名为test.conf,并修改

image-20241206141519979

  • 命令行输入(指定配置文件)
.\nginx.exe -c .\conf\test.conf
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Nginx反向代理</title>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  </head>
  <body>
    <h1>Nginx演示跨域</h1>
    <script>
      // vue react项目 配置反向代理
      // nginx
      axios
        .get(
          "/api/mmdb/movie/v3/list/hot.json?ct=%E5%B9%BF%E5%B7%9E&ci=20&channelId=4"
        )
        .then((res) => console.log(res));
    </script>
  </body>
</html>