尚硅谷 Ajax与Axios的使用与关键源码 笔记

本文详细介绍了Ajax技术,包括原生Ajax的使用、jQuery的Ajax、Axios库的应用以及Fetch API的使用。重点探讨了Ajax的跨域问题,通过JSONP和CORS两种解决方案进行阐述。此外,还深入分析了Axios的源码,包括其目录结构、请求发送函数、请求取消函数和拦截器的实现。最后,通过一个简易版的axios实现,帮助读者更好地理解Ajax的工作原理。
摘要由CSDN通过智能技术生成

Ajax概述

Ajax简介:Ajax(Async JavaScript and XML)在网页不刷新的情况下向服务器发送请求,获取响应,实现懒加载的过程

XML简介:XML与HTML类似,都是一种基于标签标记语言

  • HTML的标签都是预定义的,一般用来描述网页
  • XML没有预定义标签,标签名都是直接写来用的,一般用来存储数据,曾经的Ajax使用XML传输数据

AJAX的优缺点

  • 无需刷新就可以与服务器发送接受请求
  • 可以根据用户的事件动态更新页面内容
  • 没有浏览历史(无法后退)
  • 存在跨域问题(A网站向B网站发送内容)
  • 对SEO优化不友好

HTTP协议(详见计算机网络)
规定了浏览器与万维网服务之间的通信,规定了请求与响应,此处主要了解请求与响应报文的格式与参数

  • 请求报文结构
    • 请求行
      • 请求类型: Get/Post/Put…
      • URL: /xxx?name=zhangsan&passwd=lisi
      • HTTP协议版本: HTTP/1.1…
    • 请求头
      • Host: www.liukairui.cc
      • Cookie: username=admin
      • Content-type: text…
      • User-Agent: Chrome90
    • 空行
    • 请求体
      • 如果是GET请求,那么请求体是空的
      • 如果是POST请求,那么请求体可以是非空的
  • 响应报文结构
    • 响应行
      • HTTP协议版本: HTTP/1.1…
      • 响应状态码: 200…
      • 响应状态字符串: OK
    • 响应头
      • Content-type: text…
      • Content-encoding: gzip
      • Content-length: 1024
    • 空行
    • 响应体: 例如HTML内容

Chrome查看报文

  • F12-Network-选中包
  • 对于GET请求,可以看到
    • Header选项卡中有四个部分
      • General: 请求地址,请求方式,状态码,服务器IP,同源策略
      • Response Headers响应头
      • Request Headers请求头
      • Query String Paramenters将请求url的内容进行解析
    • Response: 响应体
    • Preview: 预览响应体
  • 对于POST请求,可以看到
    • Header选项卡中有四个部分
      • General: 请求地址,请求方式,状态码,服务器IP,同源策略
      • Response Headers响应头
      • Request Headers请求头
      • Query String Parameters: 请求体
    • Response: 响应体
    • Preview: 预览响应体

原生Ajax尝试

Ajax技术可以理解为手动在JS中进行http请求,获取响应报文,根据响应报文修改文件,与之前不同的是,之前是浏览器向服务器发送请求,服务器发送响应,浏览器刷新页面。现在是JS进行请求,JS自己处理结果,我们需要的是一套可以进行Http请求的JSAPI

请求的发送与请求头配置

GET部分

  • 服务端配置
    服务端使用的是NodeJS,我们需要Express处理http请求,其他的都不需要

    const express = require("express")
    var app=express()
    
    app.get("/server",(req,res)=>{
         
        // 配置Ajax同源策略,允许跨域访问
        res.setHeader('Access-Control-Allow-Origin','*')
        // 发回响应体
        res.send("Wow Ajax working...")
    });
    
    app.listen(,()=>{
         
        console.log("work on");
    })
    

    Node代码实现了收到一个/server请求,发回数据,由于但是没有设置页面的路由,我们需要手动打开网页

  • HTML页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <button>发送AJAX</button>
        <div class="result" style="border:solid;width: 200px;height: 200px;"></div>
    </body>
    <script>
        // 选择元素
        const btn = document.querySelector("body > button")
        var txtbox = document.querySelector(".result")
        // 绑定事件
        btn.onclick=function(){
           
            // 创建对象
            const xhr = new XMLHttpRequest();
            // 初始化对象http不可以省略
            xhr.open('GET',"http://127.0.0.1:9000/server")
            // 发送请求
            xhr.send();
            // 当xhr状态发生改变的时候时间
            // readystate表示状态分别是
            // 0: 没有初始化,1: open结束, 2: send结束, 3: 收到部分结果, 4: 收到所有结构
            xhr.onreadystatechange = function(){
           
                if(xhr.readyState===4 && xhr.status >= 200 && xhr.status < 300){
           
                    // 打印测试内容
                    console.log(xhr.status);        // 状态码
                    console.log(xhr.statusText);    // 状态字符
                    console.log(xhr.getAllResponseHeaders());   // 响应头 
                    console.log(xhr.response);      // 响应体
                    // 修改元素
                    txtbox.innerHTML=xhr.response
                }
            }
        }
    </script>
    </html> 
    
  • 总结
    我们使用Ajax实际上就是使用了一系列JS的API,包括四个步骤

    • const xhr = new XMLHttpRequest();创建一个Ajax请求
    • xhr.open('GET',"http://127.0.0.1:9000/server")初始化一个Ajax对象
    • xhr.send();发送这个对象
    • xhr.onreadystatechange绑定对象变化进行操作

    我们有几个变量表示对象状态

    • xhr.readystate: 0: 没有初始化,1: open结束, 2: send结束, 3: 收到部分结果, 4: 收到所有结构
    • xhr.status: 响应状态码
    • xhr.statusText: 响应状态字符
    • xhr.getAllResponseHeaders: 响应头
    • xhr.response: 响应体

POST部分

  • 服务端设置
    app.post("/",(req,res)=>{
         
      res.setHeader('Access-Control-Allow-Origin','*')
      res.send("OK");
    });
    
    服务端只是把get修改为了post
  • HTML设置
    事件监听函数内部修改
    const xhr = new XMLHttpRequest();
    xhr.open('POST', "http://127.0.0.1:9000");
    // 我们在这里配置我们需要发送的信息
    xhr.send('user=Liu&passwd=hey');
    xhr.onreadystatechange = function () {
         ...}
    

设置请求头

  • 设置预定义的请求头
    只需要在请求处进行修改,例如
    xhr.open(...)
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
    xhr.send(...)
    
  • 自定义请求头
    在HTML部分
    xhr.open(...)
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
    xhr.setRequestHeader('defMe', 'LiuKaieui')      // 举例,参数分别是键值
    xhr.send(...)
    
    此时,出于浏览器安全设置,我们无法发送包,并报错数据头,我们需要修改服务端
    app.all("/",(req,res)=>{
               // 其次,由于浏览器会使用get校验服务器权限,所以必须写Get方法,我们直接写成all
        res.setHeader('Access-Control-Allow-Origin','*')
        res.setHeader('Access-Control-Allow-Headers','*')   // 首先要修改这里,支持所有头
        res.send("OK");
    });
    

JSON支持

我们希望服务端向网页传送一个对象,但是默认是不支持的,很容易想到的方法就是服务端把对象转换为JSON,客户端讲JSON字符串转化为对象,有两种方法实现

  • 手动实现
    服务端只需要讲对象转换为JSON传出即可
    app.all("/json-server",(req,res)=>{
         
      let tmp = {
         "name": "Liu","Age": 12};
      res.setHeader('Access-Control-Allow-Origin','*')
      res.send(JSON.stringify(tmp));// 实际上不stringfy也可以
    });
    
    网页只需要把收到的对讲转化为对象就可以了(事件监听内部)
    const rxh=new XMLHttpRequest();
    rxh.open("POST","http://127.0.0.1:9000/json-server")
    rxh.send();
    rxh.onreadystatechange=function(){
         
        if(rxh.readyState===4 && rxh.status >=200 && rxh.status <300){
         
            console.log(JSON.parse(rxh.response));
        }
    }
    
  • 设置响应头实现
    设置响应类型后,我们就不需要手动转换了,浏览器收到JSON字符串后会自动转换,reponse就是一个对象
    const rxh=new XMLHttpRequest();
    rxh.responseType='json';     // 设置响应类型,无需设置header
    rxh.open("POST","http://127.0.0.1:9000/json-server")
    rxh.send();
    rxh.onreadystatechange=function(){
         
        if(rxh.readyState===4 && rxh.status >=200 && rxh.status <300){
         
            console.log(rxh.response);
        }
    }
    

IE缓存问题

IE(10-)会对Ajax的请求结果进行缓存,会导致下次Ajax请求得到缓存的结果,IE根据请求的url/body进行判断是否使用缓存,我们只需要在请求的时候加一个时间戳("...?t="+Date.now()),很多工具都自动实现了这个功能

请求的取消与重发

超时自动取消

可以制定获取请求的最长时间,超时后浏览器会自动取消请求。方法设置XMLHttpRequest的属性:.timeout: 网络超时时间(ms),.ontimeout: 超时回调函数函数名, .onerror: 网络错误回调函数名

  • 设置服务器, 我们设置一个2000ms的延迟模拟网络延时
    app.get("/",(req,res)=>{
         
      res.setHeader('Access-Control-Allow-Origin','*')
      setTimeout(()=>{
         res.send("You Get Response")},3000);
    })
    
  • 设置Ajax函数(事件内部的部分)
    var hrx = new XMLHttpRequest();
      // 设置超时时间,设置为2000ms的时候必然超时,4000应该不超时
      hrx.timeout=4000;
      // 超时回调函数
      hrx.ontimeout=()=>{
         alert("Network Too Slow")}
      // 网络错误回调函数
      hrx.onerror=()=>{
         alert("Network ERROR")}
      // 之后一切正常
      hrx.open("GET","http://127.0.0.1:9000");
      hrx.send();
      hrx.onreadystatechange=function(){
         
          if(hrx.readyState === 4 && hrx.status >= 200 && hrx.status < 300){
         
              txtBox.innerHTML=hrx.response
          }
      }
    

手动取消请求

  hrx.abord();

就可以直接取消

Ajax 重新发送请求

我们应该设置用户连点的时候取消上一次的请求以减小服务器压力,只要设置一个flag标记是否正在发送即可

jQuery的Ajax

  • jQuery有三个函数实现Ajax请求,分别是$.get(),$.post(),$.ajax()
  • $.get()$.post()类似,调用方法是
      $.get(
        url链接,
        {
         要发送的对象},
        (d)=>{
         收到对象的回调函数, d是获取的内容},
        "JSON"/"xml"/"html"/"text"/"script"/"json"/"jsonp" //收到数据的类型,例如这里如果写了"JSON",那么服务器发送JSON字符串,这边收到后会自动转换为对象
      )
    
  • $.ajax()是一个通用的方法
    $.ajax({
                                         // 所有的参数一起是一个对象
        url:"http://127.0.0.1:9000",        // 请求链接
        data:{
         "a":100,"b":200},             // 传输数据对象
        type:"GET",                         // 传输方式
        dataType:"JSON",                    // 数据类型
        success: (d)=>{
         console.log(d)},     // 成功回调函数
        error:()=>{
         console.log("ERR")},     // 失败回调函数
        timeout:2000,                       // 超时时间
        headers:{
                                    // 请求头 可以是标准的,也可以是自定义的
            A:100                           // 如果是自定义的要在服务端进行设置,见`使用原生...>设置请求头>自定义请求头`
        }   
    })
    
    参数还有很多,详见文档

get/post使用简单,ajax功能多,按情况使用即可

使用Axios发送Ajax[简易]

是一个热门的AJAX请求库,支持promise, 支持NodeJS,支持取消请求,支持拦截器,简易的使用方式是

axios.get("http://127.0.0.1:9000",{
      // 请求地址是一个单独的参数
    params:{
                             // 请求的参数,也就是.com?A=1&b=2那部分,使用Axios你可以不用拼串
        id:100,
        un:7
    },
    headers:{
                            // 自定义请求头
        name: 123
    },
});

获取请求结果是要使用Promise直接then(), Axios进行post的时候还可以使用params进行链接定制,但是参数列表有所不同axios.post(url,{data对象},{参数对象})

也可以使用axios函数直接发送

// 发送 POST 请求
axios({
   
  method: 'post',
  url: '/user/12345',
  data: {
   
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});

详见官方文档

使用fetch发送请求

fetch是一个window的对象,可以发送Ajax请求,返回一个Promise对象,fetch是前端发展的一种新技术产物。可以简单的理解为是XMLHttpRequest的参数简化版

Fetch API 提供了一个 JavaScript接口,用于访问和操纵HTTP管道的部分,例如请求和响应。它还提供了一个全局 fetch()方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。这种功能以前是使用 XMLHttpRequest实现的。Fetch提供了一个更好的替代方法,可以很容易地被其他技术使用,例如 Service Workers。Fetch还提供了单个逻辑位置来定义其他HTTP相关概念,例如CORS和HTTP的扩展。fetch代表着更先进的技术方向,但是目前兼容性不是很好,在项目中使用的时候得慎重。

格式是fetch(input[, init]);

  • input写url/requert对象
  • init写配置对象,有(详见文档)
    • method: 请求使用的方法,如 GET、POST。
    • headers: 请求的头信息,形式为 Headers 的对象或包含 ByteString 值的对象字面量。
    • body: 请求的 body 信息:可能是一个 Blob、BufferSource (en-US)、FormData、URLSearchParams 或者 USVString 对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。
    • mode: 请求的模式,如 cors、 no-cors 或者 same-origin。
fetch('http://example.com/movies.json')
  .then(function(response) {
   
    return response.json();
  })

在使用fetch的时候需要注意:

  • 当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。
  • 默认情况下,fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于用户 session,则会导致未经认证的请求(要发送 cookies,必须设置 credentials 选项)。

Ajax跨域

同源策略是网景提出的浏览器安全策略,他要求Ajax请求的网页与目标服务器url,端口,协议是一致的,Ajax默认遵守这个策略,违背同源就是跨域

JSONP解决跨域问题

JSONP是一个非官方的跨域解决方案,只支持GET请求,利用了<script>标签的特性实现跨域,HTML的很多标签本身就是支持跨域的,例如<img><script>会引用外部的文件

首先要在浏览器中定义一个函数用于更新内容,例如我想在跨域请求后将结果更新到#box

function process_JSONP(t){
   
  $("#box").text(t)
}

跨域被请求端设置(请求端为127.0.0.1)

app.get("/jsonp-server",(req,res)=>{
   
  str = JSON.stringfy({
   "name":"我是一个被请求的对象"})
  res.send(`process_JSONP(${
     str})`);        // JS拼串
})

请求端在process_JSONP定义后引用JS

<script src="http://127.0.0.1/jsonp-server">

这样,服务端返回字符串被当作了js,执行了process_JSONP()命令,对网页进行了更新

原生JSONP实例

我们想要实现的功能是,点击#A,浏览器发送跨域请求,服务器发送结果,收到结果后,如果是true,更新.stat背景色为绿色,否则为红色

前端JS设置

function process_jsonp(stat){
        // 获取后的处理函数
    if(stat)document.querySelector(".stat").style = "background-color:#bfa;"
    else document.querySelector(".stat").style = "background-color:#f11;"
}
btnA = document.querySelector("#A");
btnA.onclick=()=>{
   
    const jsLab = document.createElement("script");     // 创建一个script标签
    jsLab.src = "http://127.0.0.1:9000/jsonp";          // 设置一个script标签的src
    document.body.appendChild(jsLab)                    // 将script标签添加到网页
}

后端只返回true

因为我们只能写url,不能指定请求头,请求体,我们只能实现get请求

jQuery实现JSONP实例

jQuery的get/post函数默认当然是不支持跨域的,但是jQuery还有一个getJSON函数,这个函数本来是用来请求JSON,但是这个函数在jQuery底层实现的时候是先对参数的url进行解析,然后使用了上面这种script标签的方法获取JSON对象,所以这个方法是支持跨域的,我们可以利用这个特性。

首先了解函数功能,参数列表是getJSON(url,callbackFunction),当函数发现url字符串有callback=?,他会自动替换成callback=一个字符串,之后jQuery会注册一个名字叫这个字符串的方法,这个方法内容就是第二个参数于是我们利用这个特性实现JSONP

我们的目标是: 前端点击#A,后端发回消息,前端将.stat的内容替换为响应结果

前端代码

$("#A").click(()=>{
   
  $.getJSON("http://127.0.0.1:9000/jsonp?callback=?",(d)=>{
        // 这里callback=?会在请求的时候被替换
    $(".stat").text(d)
  })
})

后端代码

app.get("/jsonp",(req,res)=>{
   
  let cb = req.query.callback;          // 获取请求中的随机子复查u年
  res.send(`${
     cb}("Wow You Get!")`);    // 调用函数
})

当然这个方法是不适合大量数据请求的

CORS解决跨域问题

CORS(Cross-Origin Resource Sharing,跨域资源共享)是一种官方的跨域解决方案,不需要在客户端进行任何操作,在服务端进行修改就可以直接支持get/post

只需要在服务器上加上响应头Access-Control-Allow-Origin: *即可,如果想要设置允许特定的跨域请求,例如只允许127.0.0.1:5050的,那么只需要修改为Access-Control-Allow-Origin: 127.0.0.1:5050

实例

#btnB点击console显示获取的跨域结果

前端JS

btnB.onclick = ()=>{
   
  const xhr = new XMLHttpRequest();
  xhr.open("GET","http://127.0.0.1:9000/jsonp");
  xhr.send();
  xhr.onreadystatechange=()=>{
   
    if(xhr.readyState === 4 && xhr.status >=200 && xhr.status<300)
      console.log(xhr.response)
  }
}

后端JS

app.get("/jsonp",(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Liukairui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值