图片 2

jQuery源码分析系列(35) : Ajax – jsonp的实现与原理

需求

在调用QQ音乐搜索歌曲api的时候发现返回是一个jsonp

请求url:

图片 1

call是根据我们传递的jsonpCallback的值决定的

ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本

实现

我希望有一个jsonp对象,可以这样使用

new Jsonp({ url: 'http://xxxx', callBackname: 'call', success: function{ //处理数据 }});

 

完整代码
//基本结构!(function{ function Json{ //初始化参数 let ele = document, script = ele.createElement, result = null, _url = param.url || '', _success = param.success || function () { }, _error = param.error || function () { }; if( !param.callbackName ){ throw new Error('callbackName is required!');} window[param.callbackName] = function(){ result = arguments[0]; }; ele.getElementsByTagName[0].appendChild; script.onload = script.readystatechange = function () { if (!script.readyState || /loaded|complete/.test(script.readyState)) { _success; script.onload = script.readystatechange = null; }else{ _error(); } } script.src = _url; } window.Jsonp = Jsonp;});

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

测试(调用QQ音乐搜索API)
<!DOCTYPE html><html><head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Page Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> *{margin: 0;padding: 0} table{ width: 100%; text-align: center; border-spacing: none; border-collapse: collapse; border: 1px solid #eee; line-height: 45px; } .container{ width: 500px; margin: 100px auto; } td,th{ border: 1px solid #eee; } input{ margin-top: 10px; width: 100%; height: 35px; line-height: 35px; border: 1px solid #eee; outline: none; text-indent: 5px; } </style></head><body> <div > <table> <thead> <caption>QQ音乐搜索API调用</caption> <tr> <th>id</th> <th>name</th> <th>singer</th> </tr> </thead> <tbody > </tbody> </table> <div> <input type="text" placeholder="歌曲或者歌手..."> </div> </div> <script> !(function  { function Jsonp { let ele = document, script = ele.createElement, result = null, _url = param.url || '', _success = param.success || function () { }, _error = param.error || function () { }; if( !param.callbackName ){ throw new Error('callbackName is required!'); } window[param.callbackName] = function(){ result = arguments[0]; }; ele.getElementsByTagName[0].appendChild; script.onload = script.readystatechange = function () { if (!script.readyState || /loaded|complete/.test(script.readyState)) { _success; script.onload = script.readystatechange = null; }else{ _error(); } } script.src = _url; } window.Jsonp = Jsonp; }); document.getElementById.addEventListener('keyup',function(){ let key = document.getElementById.value; new Jsonp({ url: `https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg?format=jsonp&key=${key}&jsonpCallback=call&format=jsonp`, callbackName: 'call', success: function  { if  { render; } }, error: function(){ console.log; } }); },false); function render{ console.log; let dom = document.getElementById; let tmp = ''; result.data.song.itemlist.forEach(ele => { tmp += `<tr><td>${ele.id}</td><td>${ele.name}</td><td>${ele.singer}</td></tr>`; }); dom.innerHTML = tmp; } </script></body></html>

图片 2

 

思路

1.初始化参数2.绑定call事件(服务器返回的函数调用)来获取返回值3.动态创建script标签4.script加载完成后进行回调success并把返回值传递出去5.加载失败则回到error

本人小白一枚,代码很烂,没有处理请求参数,/捂脸
打包了在github

jquery ext dojo这类库的实现手段其实大同小异

 

在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,但img、iframe、script等标签是个例外,这些标签可以通过src属性请求到其他服务器上的数据。

 

利用script标签的开放策略,我们可以实现跨域请求数据,当然,也需要服务端的配合。

 

先看一段jQuery处理jsonp的情况

 

通过发送php请求

 

客户端

 

复制代码

$.ajax({

    async: false, // 同步加载数据,即等到ajax执行完毕再接着执行下面的语句

    url: ”, //不同的域

    type: ‘GET’, // jsonp模式只有GET是合法的

    data: {

        ‘action’: ‘aaron’

    }, // 预传参的数组

    dataType: ‘jsonp’, // 数据类型

    jsonp: ‘backfunc’, //
指定回调函数名,与服务器端接收的一致,并回传回来

    success: function(json) {

        console.log(json);

    }

})

复制代码

php服务端

 

复制代码

<?php

$act = trim($_GET[‘action’]);

 

if($act == ‘aaron’ ){

    echo trim($_GET[‘backfunc’]).'(‘.
json_encode(array(‘status’=>1,’info’=>’OK’)) .’)’;  

}

?>

复制代码

一般的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” }、

 

 

 

当我们正常地请求一个JSON数据的时候,服务端返回的是一串JSON类型的数据,而我们使用JSONP模式来请求数据的时候

 

服务端返回的是一段可执行的JavaScript代码

 

所以我们可见服务器代码最后一行

 

$_GET[‘backfunc’]).'(‘.
json_encode(array(‘status’=>1,’info’=>’OK’)) .’)

就是执行的 backfunc方法,然后把数据通过回调的方式传递过去

 

OK,就是整个流程就是:

 

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

 

 

 

jQuery的实现:

 

通过ajax请求不同域的实现,底层不是靠XmlHttpRequest而是script,所以不要被这个方法给迷惑了

 

在ajax请求中类型如果是type是get
post,其实内部都只会用get,因为其跨域的原理就是用的动态加载script的src,所以我们只能把参数通过url的方式传递

 

比如

 

复制代码

$.ajax({

    url: ”, //不同的域

    type: ‘GET’, // jsonp模式只有GET是合法的

    data: {

        ‘action’: ‘aaron’

    }, // 预传参的数组

    dataType: ‘jsonp’, // 数据类型

    jsonp: ‘backfunc’, //
指定回调函数名,与服务器端接收的一致,并回传回来

})

复制代码

其实jquery内部会转化成

 

 

然后动态加载

 

<script type=”text/javascript”
src=”;

 

然后php方就会执行backfunc(传递参数);

 

 

 

所以流程就会分二步:

 

1:针对jsonp的预处理,主要是转化拼接这些参数,然后处理缓存,因为jsonp的方式也是靠加载script所以要关闭浏览器缓存

 

inspectPrefiltersOrTransports中,当作了jsonp的预处理后,还要在执行inspect(dataTypeOrTransport);的递归,就是为了关闭这个缓存机制

 

复制代码

var dataTypeOrTransport = prefilterOrFactory(options, originalOptions,
jqXHR);

            /**

             * 针对jonsp处理

             */

            if (typeof dataTypeOrTransport === “string” &&
!seekingTransport && !inspected[dataTypeOrTransport]) {

                //增加cache设置标记

                //不需要缓存

                //dataTypes: Array[2]

                // 0: “script”

                // 1: “json”

                options.dataTypes.unshift(dataTypeOrTransport);

                inspect(dataTypeOrTransport);

                return false;

            } else if (seekingTransport) {

                return !(selected = dataTypeOrTransport);

            }

复制代码

具体的预处理的代码

 

复制代码

// Detect, normalize options and install callbacks for jsonp requests

// 向前置过滤器对象中添加特定类型的过滤器

// 添加的过滤器将格式化参数,并且为jsonp请求增加callbacks

jQuery.ajaxPrefilter(“json jsonp”, function(s, originalSettings, jqXHR)
{

 

    var callbackName,

        overwritten,

        responseContainer,

        // 如果是表单提交,则需要检查数据

        jsonProp = s.jsonp !== false && (rjsonp.test(s.url) ?

            “url” :

            typeof s.data === “string” 

发表评论

电子邮件地址不会被公开。 必填项已用*标注