【WEB】JQUERY源码分析——数据交互

1.Ajax回调方式

首先看一个demo:
给 document 绑定 ajaxStart、ajaxComplete 回调事件,trigger 绑定一个点击事件。通过 click 触发事件发送一个 ajax 请求,并且通过 complete、done、ajaxStart、ajaxComplete 返回状态回调。

//全局事件触发
$(document).ajaxSend(function() {
    show('全局事件ajaxSend ')
}).ajaxSuccess(function() {
    show("全局事件ajaxSuccess");
}).ajaxError(function() {
    show("全局事件ajaxError");
}).ajaxComplete(function() {
    show('全局事件ajaxComplete')
})

function ajax(url) {
    //执行一个异步的HTTP(Ajax)的请求。
    var ajax = $.ajax({
        url: url,
        dataType: 'script',
        //这个对象用于设置Ajax相关回调函数的上下文
        context: document.body,
        //请求发送前的回调函数,用来修改请求发送前jqXHR
        beforeSend: function(xhr) {
            xhr.overrideMimeType("text/plain; charset=x-user-defined");
            show('局部事件beforeSend')
        },
        //请求完成后回调函数 (请求success 和 error之后均调用)
        complete: function() {
            show('局部事件complete')
        },
        error: function() {
            show('局部事件error请求失败时调用此函数')
        },
        success: function() {
            show('局部事件success')
        }
    })

    ajax.done(function() {
        show('done')
    }).fail(function() {
        show('fail')
    }).always(function() {
        show('always')
    })
}

这里实现比较特别的地方,针对 ajax 提供 3 种回调方式:

1.内部回调 beforeSend, error, dataFilter, success 和 complete等
2.外部的 done、fail、when、always 等
3.全局 document 上都能捕获到 ajax 的每一步的回调通知 .ajaxStart(), .ajaxStop(),.ajaxComplete(), .ajaxError(), .ajaxSuccess(),
.ajaxSend()等

换句话说,针对ajax的请求,每一步的状态、成功、失败或者进行中,我们有 3 种方式可以监听,但是每一种还是有各自的区别:

  • Ajax的参数回调
  • 基于 deferred 方式的 done 回调
  • 全局的的自定义事件的回调

2.设计ajax库需要考虑的问题

Ajax 请求的流程:

1、通过 new XMLHttpRequest 或其它的形式(指IE)生成 ajax 的对象 xhr。
2、通过xhr.open(type, url, async, username, password) 的形式建立一个连接。
3、通过etRequestHeader 设定 xhr 的请求头部(request header)。
4、通过 send(data)请求服务器端的数据。
5、执行在 xhr 上注册的 onreadystatechange 回调处理返回数据。

开发者可能遇到的问题

1、跨域
2、json的格式
3、dataType
4、AJAX乱码问题
5、页面缓存
6、状态的跟踪
7、不同平台兼容

jQuery 在 1.5 中对 Ajax 模块的重写,增加了几个新的概念,Ajax 模块提供了三个新的方法用于管理、扩展 Ajax 请求,分别是:

  • 前置过滤器 jQuery. ajaxPrefilter
  • 请求分发器 jQuery. ajaxTransport
  • 类型转换器 ajaxConvert

除此之后还重写了整个异步队列处理,加入了 deferred,可以将任务完成的处理方式与任务本身解耦合,使用 deferreds 对象,多个回调函数可以被绑定在任务完成时执行,甚至可以在任务完成后绑定这些回调函数。这些任务可以是异步的,也可以是同步的。

例如:
1.链式反馈 done 与 fail
2.分离异步与同步处理,不再被限制到只有一个成功,失败或者完成的回调函数了。相反这些随时被添加的回调函数被放置在一个先进先出的队列中。
3.同时执行多个 Ajax 请求,这个比较复杂一点,原理其实就是 get 返回的是一个 deferred 对象,每个 jQuery 的 Ajax 方法返回值都包含一个 Promise 函数,用来跟踪异步请求。Promise 函数的返回值是 deferred 对象的一个只读视图
Deferreds 通过检测对象中是否存在 promise() 函数来判断当前对象是否可观察。$.when() 会等待所有的 Ajax
请求结束,然后调用通过 .then(),
.fail()注册的回调函数(具体调用哪些回调函数取决于任务的结束状态)。这些回调函数会按照他们的注册顺序执行。显而易见,deferred
对象就是 jQuery 的回调函数解决方案,它解决了如何处理耗时操作的问题,对那些操作提供了更好的控制,以及统一的编程接口。

3.Ajax的deferred实现

ajax 就是把 deferred 对象给掺进去可以让整个 Ajax 方法变成了一个 deferred 对象,在Ajax方法中返回的是 jqXHR 一个包装对象,在这个对象里面混入了所有实现方法。

4.前置过滤器和请求分发器

前置过滤器 jQuery. ajaxPrefilter
请求分发器 jQuery. ajaxTransport,
类型转换器 ajaxConvert

ajax在发送的过程中有一系列的处理:
1.类型转换器将服务端响应的 responseText 或 responseXML,转换为请求时指定的数据类型 dataType,如果没有指定类型就依据响应头 Content-Type 自动猜测一个。
2.jQuery 的 Ajax 是合并了 jsonp 的处理的,所以针对一些特殊的请求这里用了一个请求分发器来处理这个逻辑。

jQuery.extend({
    //前置过滤器
    ajaxPrefilter: addToPrefiltersOrTransports(prefilters),
    //请求分发器
    ajaxTransport: addToPrefiltersOrTransports(transports),
});

可见这 2 个方法是通过私有方法 addToPrefiltersOrTransports(参考右边代码一)通过 curry 手段构造的,分别是保持了 prefilters 与 transports 的引用,可见 ajaxPrefilter 就维持了addToPrefiltersOrTransports 返回函数的引用了,这种就是闭包的手法了,这也是 JS 的开发人员都需要掌握的,好处就是合并多个参数,当然因为维持引用代价就是一点点性能消耗。
当然 jQuery 不是传递的简单类型处理,还可以传递的一个引用类型的回调函数,所以针对 ajaxPrefilter 方法放闭包构件就需要做一些处理了,填充 prefilters 处理器。

5.预处理script类型

$.ajax() 调用不同类型的响应,被传递到成功处理函数之前,会经过不同种类的预处理(prefilters)。
预处理的类型取决于由更加接近默认的 Content-Type 响应,但可以明确使用 dataType 选项进行设置。如果提供了 dataType 选项, 响应的 Content-Type 头信息将被忽略。
有效的数据类型是 text, html, xml, json,jsonp,和 script

dataType:预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,比如 XML MIME 类型就被识别为 XML。在1.4中,JSON 就会生成一个 JavaScript 对象,而 script 则会执行这个脚本。随后服务器端返回的数据会根据这个值解析后,传递给回调函数。
以传入类型为script类型举例:
如果 dataType 类型为 script 的时候,需要处理:

1.执行脚本
2.内容当作纯文本返回
3.默认情况下不会通过在 URL 中附加查询字符串变量 "_=[TIMESTAMP]" 进行自动缓存结果,除非设置了 cache 参数为 true
4.在远程请求时(不在同一个域下),所有 POST 请求都将转为GET请求。(因为将使用 DOM 的 script 标签来加载)

预处理的处理就是将其缓存为设置为 false ,浏览器将不缓存此页面,这将在请求的 URL 的查询字符串中追加一个时间戳参数,以确保每次浏览器下载的脚本被重新请求,工作原理是在 GET 请求参数中附加” _={timestamp} “在请求的地址后面加一个时间戳。

6.json与jsonp

JSON:把响应的结果当作 JSON 执行,并返回一个 JavaScript 对象。如果指定的是 json,响应结果作为一个对象,在传递给成功处理函数之前使用 jQuery.parseJSON 进行解析。 解析后的 JSON 对象可以通过该 jqXHR 对象的 responseJSON 属性获得的。json 的处理只要是在 ajaxConvert 方法中把结果给转换成需要是 json 格式,这是后面的内容,这里主要研究下 jsonp 的预处理。
JSONP:是一个非官方的协议,它允许在服务器端集成 Script tags 返回至客户端,通过javascript callback 的形式实现跨域访问(这仅仅是 JSONP 简单的实现形式)。JSON 系统开发方法是一种典型的面向数据结构的分析和设计方法,以活动为中心,一连串的活动的顺序组合成一个完整的工作进程。
jsonp出现的根源:
1.跨域这个问题的产生根本原因是浏览器的同源策略限制,理解同源策略的限制同源策略是指阻止代码获得或者更改从另一个域名下获得的文件或者信息。也就是说我们的请求地址必须和当前网站的地址相同。同源策略通过隔离来实现对资源的保护,解决这个限制的一个相对简单的办法就是在服务器端发送请求,服务器充当一个到达第三方资源的代理中继。虽然是使用广泛但是这个方法却不够灵活。
2.另一个办法就是使用框架(frames),将第三方站点的资源包含进来,但是包含进来的资源同样要受到同源策略的限制。
3.有一个很巧妙的办法就是在页面中使用动态代码元素,代码的源指向服务地址并在自己的代码中加载数据。当这些代码加载执行的时候,同源策略就不会起到限制。但是如果代码试图下载文件的时候执行还是会失败,幸运的是,我们可以使用JSON(JavaScript Object Notation)来改进这个应用。

function showPrice(data){ 
    alert("Symbol:" + data.symbol + ", Price:" + data.price)
}

var data = {symbol:'IBM', price:100}; 
showPrice(data);

1.代码通过动态加入 Javascript 代码,来执行函数加载数据。正如之前提到过的,同源策略对于动态插入的代码不适用。也就是你可以从不同的域中加载代码,来执行在他们代码中的 JSON 数据。这就是 JSONP(JSON with Padding)。注意,使用这种方法时,你必须在页面中定义回调函数,就像上例中的 showPrice 一样。

2.我们通常所说的 JSONP 服务(远程 JSON 服务),实际上就是一种扩展的支持在用户定义函数中包含返回数据的能力。这种方法依赖于必须接受一个回调函数的名字作为参数。然后执行这个函数,处理 JSON 数据,并显示在客户页面上。

所以总结其实 json 的一个核心点:允许用户传递一个 callback 参数给服务端,然后服务端返回数据时会将这个 callback 参数作为函数名来包裹住 JSON 数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

7.jsonp的原理

ajax 和 jsonp 区别如下: 
1.ajax 的核心是通过 XmlHttpRequest 获取非本页内容。
2.jsonp 的核心则是动态添加 <script> 标签来调用服务器提供的 js 脚本,允许用户传递一个 callback 参数给服务端,然后服务端返回数据时会将这个 callback 参数作为函数名来包裹住 JSON 数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

query、ext、dojo 这类库的实现手段其实大同小异,在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,但 img、iframe、script 等标签是个例外,这些标签可以通过 src 属性请求到其他服务器上的数据。利用 script 标签的开放策略,我们可以实现跨域请求数据,当然,也需要服务端的配合。一般的 ajax 是不能跨域请求的,因此需要使用一种特别的方式来实现跨域,其中的原理是利用 script元素的这个开放策略。

这里有2个重要的参数:
jsonpCallback:
为 jsonp 请求指定一个回调函数名。这个值将用来取代 jQuery 自动生成的随机函数名。这主要用来让 jQuery 生成一个独特的函数名,这样管理请求更容易,也能方便地提供回调函数和错误处理。你也可以在想让浏览器缓存 GET 请求的时候,指定这个回调函数名。从jQuery 1.5 开始,你也可以使用一个函数作为该参数设置,在这种情况下,该函数的返回值就是 jsonpCallback 的结果。
jsonp:
在一个 jsonp 请求中重写回调函数的名字。这个值用来替代在 “callback=?” 这种 GET 或 POST 请求中 URL 参数里的 “callback” 部分,比如 {jsonp:’onJsonPLoad’} 会导致将 “onJsonPLoad=?” 传给服务器。在 jQuery 1.5,设置 jsonp 选项为 false,阻止了 jQuery 从加入 “?callback” 字符串的 URL 或试图使用 “=?” 转换。在这种情况下,你也应该明确设置 jsonpCallback 设置。例如, { jsonp: false, jsonpCallback: “callbackName” }。

整个流程为:
客户端发送一个请求,规定一个可执行的函数名(这里就是 jQuery 做了封装的处理,自动帮你生成回调函数并把数据取出来供 success 属性方法来调用,不是传递的一个回调句柄),服务端接受了这个 backfunc 函数名,然后把数据通过实参的形式发送出去

8.jsonp的实现

$.ajax({
    crossDomain:true,//强制跨域
    url: ' http://url...’, //不同的域
    type: 'GET', // jsonp模式只有GET是合法的
    data: {
        'action': 'aaron'
    }, // 预传参的数组
    dataType: 'jsonp', // 数据类型
    jsonp: 'backfunc', // 指定回调函数名,与服务器端接收的一致,并回传回来
})

这里有几个要注意的:
在 ajax 请求中类型如果是 type 是 post,其实内部都只会用 get,因为其跨域的原理就是用的动态加载 script 的 src,所以我们只能把参数通过 url 的方式传递
我们使用了 dataType 是 ‘jsonp’ 但是 jquery 内部有进一步的优化,如果探测到还是同域下的请求,依然还是用 XmlHttpRequest 处理,所以我们在同域下测试的话,可以把 crossDomain 选项置为 true,这样强制为跨域处理,这样就会通过 script 处理了,那么根据 jsonp 的原理其实 jquery 内部会把 URL 最终会转化成:

http://192.168.1.113:8080/github/jQuery/jsonp.php?callback=flightHandler&amp;action=aaron&amp;_=1418782732584 ">

然后通过创建脚本动态加载:

<script type="text/javascript" src=" http://192.168.1.113:8080/github/jQuery/jsonp.php?callback=flightHandler&amp;action=aaron&amp;_=1418782732584 "></script>

要处理的几个问题:
1. 采用的是脚本请求的方法,所以虽然 dataType 是 ‘jsonp’ 但是内部还是按照 script 处理
2. get 请求的后缀拼接,编码的处理
3. 避免缓存的处理

所以流程就会分二步:
1.针对 jsonp 的预处理,主要是转化拼接这些参数,然后处理缓存,因为 jsonp 的方式也是靠加载 script 所以要关闭浏览器缓存
2.inspectPrefiltersOrTransports中jsonp 的预处理后,还要在执行 inspect(dataTypeOrTransport); 的递归,就是为了关闭这个缓存机制
jquery 通过预处理会在 window 对象中加载一个全局的函数,当代码插入时函数执行,执行完毕后就会被移除。同时 jquery 还对非跨域的请求进行了优化,如果这个请求是在同一个域名下那么他就会像正常的 Ajax 请求一样工作。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值