zoukankan      html  css  js  c++  java
  • jQuery ajax —— 将类AJAX方法包装起来

    上一篇文章,将jQuery.ajax中的一些细节补充完。这篇文章讲解如果将类AJAX方法都包装进jQuery.ajax中。下篇文章则讲解各预过滤器和分发器的细节。

    为什么要包装起来? 

    我们知道,古老的XMLHttpRequest出于同源策略考虑,是不支持跨域的。所以,在前端想动态加载跨域Javascript脚本,通常是使用被称为Script DOM Element的方案,如:

    var scriptElem = document.createElement("script");
    scriptElem.src = "http://anydomain.com/A.js";
    document.getElementsByTagName("head")[0].appendChild(scriptElem);

    同理,JSON也无法通过XMLHttpRequest进行跨域,所以利用Script DOM Element,将JSON填入一个回调函数中来实现其跨域,也就是JSONP(JSON with padding, 填充式JSON或参数式JSON)。

    实际上JSONP就是将,得到JSON后回调的函数通过GET传参告诉服务器,然后服务器拼接一段脚本,用该回调函数并参数为需要的JSON数据,如:

    callback({ "name": "Justany_WhiteSnow" });

    jQuery团队当然希望开发者开发的时候,不需要想需不需要跨域,只要直接使用就行了。

    所以他们把XMLHttpRequest、Script DOM Element、JSONP包装起来,都当成AJAX来使用。

    这里顺便提一下,其实现代浏览器(Firefox 3.5+、Safari 4+、Chrome等)中,通过XMLHttpRequeest实现了CORS(Cross-Origin Resource Sharing, 跨源资源共享)原生支持。也就是XMLHttpRequest在某些浏览器中,实际上是可以跨域的,只需要设置一下HTTP Response Header中的Access-Control-Allow-Origin。比如设置成通配符*。

    而IE8也引入XDomainRequest也实现了CORS。

    但毕竟某些浏览器不行,所以,咳咳……这不能成为一种通用方案。

    怎么包装起来? 

    首先我们有一个山寨XHR对象,也就是jqXHR对象。通过对其添加send、abort来模拟XHR对象。

    可是我们需要在不同方案执行前先处理一下特异性的东东,所以我们需要一个预过滤机制(Prefilter)来预先处理一下。

    然后我们需要知道到底应当用那一套方案来执行整个过程,所以我们需要一个分发机制(Transport)来得到最后的jqXHR对象。

    inspectPrefiltersOrTransports

    我们在jQuery.ajax找到了预过滤和分发机制的函数,inspectPrefiltersOrTransports。

    // 预过滤
    inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
    
    //……
    
    // 得到transport
    transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );

    然后我们看看这个函数在干些什么。

    // 检测函数,预过滤或者分发器
    function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
    
        // 定义个参数对象
        var inspected = {},
            // 看看传进来的结构体是prefilters, 还是transports
            // 如果是prefilters,证明这是预过滤过程,如果是transports分发过程
            // 所以这个是,是不是在分发过程
            seekingTransport = ( structure === transports );
    
        function inspect( dataType ) {
            var selected;
            // 将inspected的dataType对应属性设置成true
            inspected[ dataType ] = true;
            // 遍历prefilters对应dataType对象中的所有过滤器或者转换器工厂函数
            jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
                // 得到dataType或者转换器
                var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
                // 如果dataType为字符串,即上面过程是过滤器
                // 如果在预过滤过程
                // 并且这个过滤出来的dataType不等于刚开始传进来的dataType
                if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
                    // 将现在这个新的dataType插入到options中
                    options.dataTypes.unshift( dataTypeOrTransport );
                    // 检测新的dataType
                    inspect( dataTypeOrTransport );
                    return false;
                // 否则如果在分发过程
                } else if ( seekingTransport ) {
                    // 定义selected为dataTypeOrTransport
                    return !( selected = dataTypeOrTransport );
                }
            });
            
            return selected;
        }
    
        // 检查dataTypes数组的第一个,如果结果是undefined,
        // 则看看上面检查的是不是通配符*,如果不是则检查通配符
        return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
    }

    我们可以看到,这个函数实际上就是从prefilters和transports中取出对应dataType的东东,然后过滤或者分发。

    那么怎么定义prefilters和transports这两个对象的呢?

    jQuery.ajaxPrefilter & jQuery.ajaxTransport

    实际上,jQuery是通过这两个接口来定义上面两个对象的内容的,一个是定义预过滤器,另一个则定义分发器。

    jQuery.ajaxPrefilter = addToPrefiltersOrTransports( prefilters );
    jQuery.ajaxTransport = addToPrefiltersOrTransports( transports );

    而这两个方法都是由addToPrefiltersOrTransports生成的。

    addToPrefiltersOrTransports

    // jQuery.ajaxPrefilter和jQuery.ajaxTransport的构造函数
    function addToPrefiltersOrTransports( structure ) {
    
        // dataTypeExpression是可选参数,缺省值为*
        return function( dataTypeExpression, func ) {
            
            // 如果dataTypeExpression不是字符串
            // 模拟重载
            if ( typeof dataTypeExpression !== "string" ) {
                func = dataTypeExpression;
                // 缺省为*
                dataTypeExpression = "*";
            }
    
            var dataType,
                i = 0,
                // 将dataTypeExpression转为小写,并用空白拆成数组
                dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || [];
    
            // 如果func是一个函数
            if ( jQuery.isFunction( func ) ) {
                // 遍历dataTypeExpression中的所有dataType
                while ( (dataType = dataTypes[i++]) ) {
                    // 如果有必要则删除头部的+号
                    if ( dataType[0] === "+" ) {
                        // 删除+号
                        dataType = dataType.slice( 1 ) || "*";
                        // 往structure对应的dataType中推入func函数
                        (structure[ dataType ] = structure[ dataType ] || []).unshift( func );
    
                    // 否则不需要处理
                    } else {
                        // 往structure对应的dataType中推入func函数
                        (structure[ dataType ] = structure[ dataType ] || []).push( func );
                    }
                }
            }
        };
    }

    接下来就是通过调用jQuery.ajaxPrefilter和jQuery.ajaxTransport方法,添加预过滤器和分发器来完成包装。

  • 相关阅读:
    centos7部署jmeter+grafana+telegraf+prometheus/influxdb,构建性能测试可视化实时监控(三)
    python+uvicorn+fastapi (二)
    python+uvicorn+fastapi (一)
    vue系列 <router-link>属性
    vue系列 vue-router安装配置呈现DEMO
    vue系列 url的hash和HTML5的history
    vue系列 前后端渲染+前后端路由
    python框架Django实战商城项目之工程搭建
    Socket--selecct的用法
    迭代器与生成器的区别
  • 原文地址:https://www.cnblogs.com/justany/p/2981523.html
Copyright © 2011-2022 走看看