Ajax学习

本文详细介绍了Ajax技术,包括其概念、特点及优缺点,并深入探讨了XMLHttpRequest对象的使用方法,同时对比了fetch和axios两种现代Ajax请求方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Ajax的学习

  • 什么是Ajax
    • ajax全称是async javascript + xml 即 异步JavaScript加xml技术
    • 特点:实现页面动态刷新
    • 优点:可以无需刷新页面与服务端进行通信,可允许根据用户事件来更新部分页面
    • 缺点:没有浏览历史,不能后退。存在跨域问题
  • 什么是XML
    • 可扩展标记语言,被设计用来传输以及存储数据
    • xml和html类似,不同的是html是预定义标签而xml中没有预定义标签,全都是自定义标签,用来表示一些数据

http请求

  • 请求报文:格式
    1. 请求行:请求类型 请求参数 http版本号
    2. 请求头:Host: xxx 等
    3. 空格
    4. 请求体
  • 响应报文
    1. 响应行: http版本号 状态码 响应状态字符串
    2. 响应头
    3. 空格
    4. 请求体

XHR对象

  • xhr对象:XMLHttpRequest对象
  • 它的使用方法:
    1. 创建对象:let xhr = new XMLHttpRequest()
    2. 初始化: xhr.open('请求方式','请求地址','请求是否异步')
    3. 发送: xhr.send(null)发送请求,如果没有请求体的话,send内必须参数为null,有的话,send内为请求体内容
    4. 事件绑定:接受并处理服务端返回的结果xhr.onreadystatechange=function(){}为保证跨浏览器的兼容性,onreadystatechange要在open之前调用、
  • 如果接受到响应数据后,xhr对象的下列属性会被赋值
    1. responseText: 作为响应体返回的位文本
    2. responseXml: 如果响应的内容类型是text/xml或者application/xml时,就是包含响应数据的xml dom文档
    3. status: 响应的http状态
    4. statusText:响应的http状态描述
  • 在异步情况下:我们不应阻塞代码运行,xhr对象给我们准备了一个readyState属性:表示当前处于请求的哪个阶段。我们可以通过监听readyState改变触发的readystatechange事件处理程序
    • 0:未初始化(uninitialized)(尚未调用open方法)
    • 1:已打开(open),已调用open,但是没有调用send
    • 2:已发送(send),已发送但是并未接收响应
    • 3:接收中(receiving)已经收到部分响应
    • 4:完成(complete)已接收到所有响应
    xhr.onreadystatechange = function(){
    	if(xhr.readyState == 4){
    		if(xhr.status){
    			// 根据status(响应状态码,处理响应逻辑)
    		}
    	}
    }
    
  • 完整的一个ajax请求
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
    	if(xhr.readtState==4){
    		if(xhr.status==200){
    			//成功接收到数据
    		}
    	}
    }
    xhr.open('get','http:xxxx',true) // get请求,参数可以直接拼接到url上,post参数在send内
    xhr.send(null)
    

xhr.setRequestHeader :自定义请求头内容

  • xhr发送请求默认会有下面头部字段
    1. Accept:浏览器可以处理的内容类型
    2. Accept-Charset:浏览器可以显示的字符集
    3. Accept-Encoding:浏览器可以处理的压缩编码类型
    4. Accept-Language:浏览器使用的语言
    5. Connection:浏览器与服务器的连接类型
    6. Cookie:页面中设置的cookie
    7. Host: 发送请求的页面所在的域
    8. Referer:发送请求的页面的uri

XHR超时

  • xhr.timeout 请求超时时间
  • xhr.ontimeout = function(){} :请求超时的回调
  • 请求超时之后,readtState也会变成4,也会调用onreadrstatechange事件,不过访问xhr.status属性会报错,此时可以try catch语句包裹 onreadrstatechange事件

XHR进度事件

  • loadstart:在接收到响应的第一个字节时触发
  • progress: 在接收响应期间来回触发:该方法接收一个对象参数event,该参数有三个属性 lengthComputable表示进度是否可用,position接收到的字节数,totalSize响应头定义的总字节数
  • error:在请求出错时触发
  • abort:在调用abort() 终止连接时触发
  • load: 在成功接收完响应时触发
  • loadend: 通讯完成时,且在error abort load之后触发

jquery中的ajax

  • 对js原生的Ajax进行了封装,在封装后的Ajax的操作更加简洁,功能更加强大
  • jQuery中常见的ajax请求
    • $.get(url, [data], [callback], [type])
    // url:待载入页面的URL地址
    // data:待发送 Key/value 参数。
    // callback:载入成功时回调函数。
    // type:返回内容格式,xml, html, script, json, text, _default。
    $.get("test.cgi", { name: "John", time: "2pm" },
          function(data){
          alert("Data Loaded: " + data);
    });
    
    • $.post(url, [data], [callback], [type])
    // url:待载入页面的URL地址
    // data:待发送 Key/value 参数。
    // callback:载入成功时回调函数。
    // type:返回内容格式,xml, html, script, json, text, _default。
    $.post("test.php", { "func": "getNameAndTime" },
          function(data){
          alert(data.name); // John
          console.log(data.time); //  2pm
          }, "json");
    
    • $.ajax(url,[settings]),返回其创建的 XMLHttpRequest 对象。大多数情况下你无需直接操作该函数,除非你需要操作不常用的选项,以获得更多的灵活性。jQuery.ajax
    // 简单的使用方法如下:其他具体配置请查看官网
    $.ajax({
       type: "POST",
       url: "some.php",
       data: "name=John&location=Boston",
       success: function(msg){
         alert( "Data Saved: " + msg );
       }
    });
    
    • $.getScript(url,[callback]) 通过 HTTP GET 请求载入并执行一个 JavaScript 文件。
    jQuery.getScript("https://dev.jquery.com/view/trunk/plugins/color/jquery.color.js", function(){
      $("#go").click(function(){
        $(".block").animate( { backgroundColor: 'pink' }, 1000)
          .animate( { backgroundColor: 'blue' }, 1000);
      });
    });
    
    • $.getJSON(url,[data],[fn]) 通过 HTTP GET 请求载入 JSON 数据。
    $.getJSON("test.js", { name: "John", time: "2pm" }, function(json){
      alert("JSON Data: " + json.users[3].name);
    });
    

fetch

  • fetch()是 XMLHttpRequest 的升级版,用于在 JavaScript 脚本里面发出 HTTP 请求。浏览器原生提供这个对象
  • fetch()的功能与 XMLHttpRequest 基本相同,但有三个主要的差异
    • fetch()使用 Promise,不使用回调函数
    • fetch()采用模块化设计,API 分散在多个对象上(Response 对象、Request 对象、Headers 对象)
    • fetch()通过数据流(Stream 对象)处理数据,就是fetch返回回来的response对象是一个Stream 对象,需要通过response.json()是一个异步操作,取出所有内容,并将其转为 JSON 对象。
  • Response 对象:处理 HTTP 回应
    • fetch()请求成功以后,得到的是一个 Response 对象。它对应服务器的 HTTP 回应。如果想要通过response对象包含的数据需要通过Stream 接口异步读取,但是对于response包含的同步属性,可以直接读取(HTTP回应的headers)
    // Response.ok 属性返回一个布尔值,表示请求是否成功
    // Response.status 属性返回一个数字 表示 HTTP 回应的状态码
    // Response.statusText 属性返回一个字符串,表示 HTTP 回应的状态信息
    // Response.url 属性返回请求的 URL。如果 URL 存在跳转,该属性返回的是最终 URL。
    // Response.type 属性返回请求的类型:basic,cors,error等,这里不是get/post
    // Response.redirected  属性返回一个布尔值,表示请求是否发生过跳转。
    async function fetchText() {
      let response = await fetch('/readme.txt');
      console.log(response.status); 
      console.log(response.statusText);
    }
    // response.text():得到文本字符串。
    // response.json():得到 JSON 对象。
    // response.blob():得到二进制 Blob 对象。
    // response.formData():得到 FormData 表单对象。
    // response.arrayBuffer():得到二进制 ArrayBuffer 对象。
    
  • fetch的语法
    // fetch()的第一个参数是 URL,还可以接受第二个参数,作为配置对象,定制发出的 HTTP 请求。
    fetch(url) 
    fetch(url, optionObj)
    fetch(url, {
      method: "GET",
      headers: {
        "Content-Type": "text/plain;charset=UTF-8"
      },
      body: undefined,
      referrer: "about:client", // 属性用于设定fetch()请求的referer标头。
      referrerPolicy: "no-referrer-when-downgrade", // 属性用于设定Referer标头的规则
      mode: "cors", // 属性指定请求的模式。
      credentials: "same-origin", // 属性指定是否发送 Cookie。
      cache: "default", // 属性指定如何处理缓存
      redirect: "follow", // 属性指定 HTTP 跳转的处理方法。
      integrity: "", // 属性指定一个哈希值,用于检查 HTTP 回应传回的数据是否等于这个预先设定的哈希值。
      keepalive: false, // 属性用于页面卸载时,告诉浏览器在后台保持连接,继续发送数
      signal: undefined //属性指定一个 AbortSignal 实例
    });
    
  • fetch()请求的底层用的是 Request() 对象的接口,参数完全一样,因此上面的 API 也是Request()的 API。所以上面的optionObj 可以是 new Request()的实例对象
  • fetch()请求发送以后,如果中途想要取消,需要使用AbortController对象
    let controller = new AbortController();
    setTimeout(() => controller.abort(), 1000);
    
    try {
      let response = await fetch('/long-operation', {
        signal: controller.signal
      });
    } catch(err) {
      if (err.name == 'AbortError') {
        console.log('Aborted!');
      } else {
        throw err;
      }
    }
    

axios

  • axios特点
    • 基于promise的异步ajax请求
    • 浏览器端/node端都可以使用
    • 支持请求/响应拦截器
    • 支持请求取消
    • 请求/响应数据转换
    • 批量发送多个请求
  • axios常用语法
    • axios(config) :通用的/最本质的发任意类型请求的方式

    • axios(url[,config]):可以指定url发送get请求

    • axios.request(config): 等同于axios(config)

    • axios.get(url[,config]): 发get请求

    • axios.delete(url[,config]): 发delete请求

    • axios.post(url[,config]): 发post请求

    • axios.put(url[,config]): 发put请求

    • axios.defaults.xxxx 请求的默认全局配置

    • axios.interceptors.request.use() 添加请求拦截器

    • axios.interceptors.response.use() 添加响应拦截器

    • axios.create([config]) 创建一个新的axios,下面几个方法通过create创建的实例不能使用

    • axios.Cancel :用于创建取消请求的错误对象

    • axios.CancelToken:用于创建取消请求的token对象

    • axios.isCancel: 是否是一个取消请求的错误,在error的时候判断是正常的请求失败还是说通过Cancel创建的cancel错误对象

    • axios.all:用于批量执行多个异步请求

    • axios.spread:用来收所有成功数据的回调函数的方法

axios中的重点方法

  • axios.create当有多个后端端口时每个端口时如果单纯使用axios太麻烦使用axios.create就非常的方便
  • axios.interceptors.request.use() 请求拦截
  • axios.interceptors.response.use() 响应拦截
  • axios.CancelToken;取消请求:在config的配置里面新增一个cancelToken属性,该属性使用如下
    const CancelToken = axios.CancelToken;
    let cancel;
    
    axios.get('/user/12345', {
      cancelToken: new CancelToken(function executor(c) {
        // executor 函数接收一个 cancel 函数作为参数
        cancel = c;
      })
    });
    
    // cancel the request
    cancel("强制请求失败");  // 该参数可以通过在promise中的error获取到,不过是一个Cancel对象
    

关于axios插件的分析

  • axios为什么既可以函数调用,还能当作对象获取属性,并且它与Axios构造函数的关系

    • 首先源码里面通过有Axios构造函数
      function Axios(instanceConfig) {
        this.defaults = instanceConfig;
        this.interceptors = {
          request: new InterceptorManager(),
          response: new InterceptorManager()
        };
      }
      // Axios原型上的request方法,所有的请求都是通过该方法发出的,axios.get/axios.post等也是通过该方法传递method为get/post请求的
      Axios.prototype.request = function request(configOrUrl, config) {
      }
      
      // 下面通过createInstance构造方法创建一个content(Axios实例)
      function createInstance(defaultConfig) {
       var context = new Axios(defaultConfig);
      // 通过bind方法将Axios.prototype,request方法,绑定到context上,返回一个新函数赋值给instance
        var instance = bind(Axios.prototype.request, context);
      
        // Copy axios.prototype to instance
       // 拷贝axios.prototype原型上的方法到instance上
        utils.extend(instance, Axios.prototype, context);
      	// 拷贝Axios实例context属性到instance
        // Copy context to instance
        utils.extend(instance, context);
      	// 通过以上步骤,axios就能当函数使用,也能当对象使用
      	// 从语法上说axios不是Axios的实例
      	// 但通过以上方式它又有Axios的原型方法以及属性,所以从功能上来说,axios是Axios的实例
        // Factory for creating new instances
        instance.create = function create(instanceConfig) {
          return createInstance(mergeConfig(defaultConfig, instanceConfig));
        };
      
        return instance;
      }
      // Expose Axios class to allow class inheritance
      axios.Axios = Axios;
      
      // Expose Cancel & CancelToken
      axios.CanceledError = require('./cancel/CanceledError');
      axios.CancelToken = require('./cancel/CancelToken');
      axios.isCancel = require('./cancel/isCancel');
      axios.VERSION = require('./env/data').version;
      
      // Expose AxiosError class
      axios.AxiosError = require('../lib/core/AxiosError');
      
      // alias for CanceledError for backward compatibility
      axios.Cancel = axios.CanceledError;
      
      // Expose all/spread
      axios.all = function all(promises) {
      return Promise.all(promises);
      };
      axios.spread = require('./helpers/spread');
      
      // Expose isAxiosError
      axios.isAxiosError = require('./helpers/isAxiosError');
      
      module.exports = axios;
      
  • axiosaxios.create创建出来的实例的区别

    • 通过上面代码可以看到axios创建出来之后,会有其他方法以及属性的挂载,而axios.create创建出来的实例没有其他方法的挂载
    • 他们都是通过createInstance构造函数创建出来的
  • axios()函数以及axios.request()以及其他请求axios.get()/axios.post()的关系

    • axios通过createInstance构造函数创建出来之后,在通过bind(Axios.prototype.request, context)方式让其可以使用request方法,所以说axios方法执行和Axios.prototype.request方法执行是同一个方法
    • axios.get()/axios.post()通过下面代码可知,get以及post方式请求内部也是调用了Axios.prototype.request方法
      utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
        /*eslint func-names:0*/
        Axios.prototype[method] = function(url, config) {
          return this.request(mergeConfig(config || {}, {
            method: method,
            url: url,
            data: (config || {}).data
          }));
        };
      });
      
      utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
        /*eslint func-names:0*/
        Axios.prototype[method] = function(url, data, config) {
          return this.request(mergeConfig(config || {}, {
            method: method,
            url: url,
            data: data
          }));
        };
      
  • axios运行的整体流程
    在这里插入图片描述

  • axios中最重要的三个函数

    • request 串联整个流程
      • 这个方法中最重要的是requestInterceptorChain请求拦截器数组,responseInterceptorChain响应拦截器数据,chain[dispatchRequest, undefined]这三个数组
      • 在未串联流程之前,创建了一个resolve状态的promise(config)
      • 代码中通过unshift以及concat方法把请求拦截器数组,chain,响应拦截器数组组合一起,这样就把请求拦截,请求,响应拦截串联一起,通过promise.then方法进行流程
      • 请求拦截器的执行过程是后添加先执行(unshift),而响应拦截器``的先后是先添加先执行(concat)
      • 然后while循环调用then方法,最终返回一个``最终结果的promise
      Axios.prototype.request = function request(configOrUrl, config) {
       /*eslint no-param-reassign:0*/
       // Allow for axios('example/url'[, config]) a la fetch API
        if (typeof configOrUrl === 'string') {
          config = config || {};
          config.url = configOrUrl;
        } else {
          config = configOrUrl || {};
        }
      
        config = mergeConfig(this.defaults, config);
      
        // Set config.method
        if (config.method) {
          config.method = config.method.toLowerCase();
        } else if (this.defaults.method) {
          config.method = this.defaults.method.toLowerCase();
        } else {
          config.method = 'get';
        }
      
        var transitional = config.transitional;
      
        if (transitional !== undefined) {
          validator.assertOptions(transitional, {
            silentJSONParsing: validators.transitional(validators.boolean),
            forcedJSONParsing: validators.transitional(validators.boolean),
            clarifyTimeoutError: validators.transitional(validators.boolean)
          }, false);
        }
      	
        // filter out skipped interceptors
        var requestInterceptorChain = [];
        var synchronousRequestInterceptors = true;
        this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
          if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
            return;
          }
      
          synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
      
          requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
        });
      
        var responseInterceptorChain = [];
        this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
          responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
        });
      
        var promise;
      
        if (!synchronousRequestInterceptors) {
          var chain = [dispatchRequest, undefined];
      
          Array.prototype.unshift.apply(chain, requestInterceptorChain);
          chain = chain.concat(responseInterceptorChain);
      
          promise = Promise.resolve(config);
          while (chain.length) {
            promise = promise.then(chain.shift(), chain.shift());
          }
      	
      return promise;
      }
      
      
      var newConfig = config;
      while (requestInterceptorChain.length) {
      var onFulfilled = requestInterceptorChain.shift();
      var onRejected = requestInterceptorChain.shift();
      try {
        newConfig = onFulfilled(newConfig);
      } catch (error) {
        onRejected(error);
        break;
      }
      }
      
      try {
      promise = dispatchRequest(newConfig);
      } catch (error) {
      return Promise.reject(error);
      }
      
      while (responseInterceptorChain.length) {
      promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift());
      }
      
      return promise;
      };
      
    • dispatchRequest:数据处理函数,该函数是在request的循环过程中执行的在该函数中首先处理请求参数,然后返回一个return adapter(config).then(function onAdapterResolution(response) {}函数,该函数中进行响应数据处理,而adapter就是封装XMLRequest请求对象的函数
      module.exports = function dispatchRequest(config) {
      //处理请求参数对象
      // Transform request data
      config.data = transformData.call(
      config,
      config.data,
      config.headers,
      config.transformRequest
      );
      var adapter = config.adapter || defaults.adapter;
      return adapter(config).then(function onAdapterResolution(response) {
      // Transform response data
      // 处理响应数据
      response.data = transformData.call(
        config,
        response.data,
        response.headers,
        config.transformResponse
      );
      
      return response;
      }, function onAdapterRejection(reason) {
      if (!isCancel(reason)) {
        throwIfCancellationRequested(config);
      
        // Transform response data
        if (reason && reason.response) {
          reason.response.data = transformData.call(
            config,
            reason.response.data,
            reason.response.headers,
            config.transformResponse
          );
        }
      }
      
      return Promise.reject(reason);
      });
      };
      
    • xhrAdaper:实际就是xhr对象的封装
  • 以上就是axios过程的整体流程

同源策略

  • 所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源
  • 同源策略即:不同源之间的页面,不准互相访问数据。

解决跨域的方式

jsonp:通过script标签能够跨域的特性,实现请求的跨域

cros解决跨域,通过后端配置实现cros

  • CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
  • 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。
  • 实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
  • 浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。浏览器对这两种请求的处理,是不一样的
    • 对于简单请求,浏览器直接发出CORS请求,在头信息之中,增加一个Origin字段。Origin字段来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
    • 如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了
    • 如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。Access-Control-Allow-Origin: xxx Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: xxxx
      • Access-Control-Allow-Origin:该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求
      • Access-Control-Allow-Credentials:该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。
      • Access-Control-Expose-Headers:该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader(‘FooBar’)可以返回FooBar字段的值。
    • 非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
      • 浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息。
      OPTIONS /cors HTTP/1.1 // "预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的
      Origin: http://api.bob.com
      Access-Control-Request-Method: PUT // 该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法
      Access-Control-Request-Headers: X-Custom-Header // 该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header。
      Host: api.alice.com
      Accept-Language: en-US
      Connection: keep-alive
      User-Agent: Mozilla/5.0....
      
      • 服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应
      HTTP/1.1 200 OK
      Date: Mon, 01 Dec 2008 01:15:39 GMT
      Server: Apache/2.0.61 (Unix)
      Access-Control-Allow-Origin: http://api.bob.com  //表示http://api.bob.com可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。
      Access-Control-Allow-Methods: GET, POST, PUT  // 表明服务器支持的所有跨域请求的方法
      Access-Control-Allow-Headers: X-Custom-Header // 它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
      Content-Type: text/html; charset=utf-8
      Content-Encoding: gzip
      Content-Length: 0
      Keep-Alive: timeout=2, max=100
      Connection: Keep-Alive
      Content-Type: text/plain
      // Access-Control-Max-Age 用来指定本次预检请求的有效期,
      

nginx反向代理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值