正在加载中

最后更新于 2019年09月18日

如果要在jq里进行跨域,一般来说只有使用jsonp,另一种方式则是进行中间层欺骗,也就是所谓的反代。

这里我们主要讲讲json是怎么回事

基本json

实现jsonp全部都是使用带有src标签的元素,一般默认使用script元素,在src链接地址中,在地址后面加上?name=value&callbakc=hd;其中name=value是传值,而callbakc=hd则是回调,在本地js全局作用域下创建一个名为hd的函数。

var script = document.createElement("script");
script.src = "xxxx?callback=hd";
document.getElementsByTagName('head')[0].appendChild(script);


//回调函数
function hd(data) {
  console.log(data);
}

//或者使用变量名赋值
var hd = function(data) {
  console.log(data);
}

重点就是这个回调函数,hd带有一个参数,这个参数就是服务器通过动态创建并返回给你的,因为script元素在加载完毕后执行内部代码,而内部等于hd(data),就是一个执行hd的代码段,于是你之前创建的hd函数成为了回调函数并运行。

注意:因为json的文件格式js是支持的,所以回调函数执行后并不会提示警告,而如果服务器动态创建给你的是html类型的文件,那么虽然能通过script元素加载运行,但是会提示你(即使其 MIME 类型(“text/html”)不是有效的 JavaScript MIME 类型,仍已加载来自xxx的脚本)

这里就有人想了,那jq不是有一个dataType的属性吗,可以告诉服务器我期望返回的文件类型,然而这种想法是不对的,我们下面来讲到他真正的作用。

jq的jsonp

$.ajax({
     url:'xxx',
     type : 'GET',
     data:{
          key : "xxx",
          com : "xxx",
          no : "xx"
      },
    dataType : 'jsonp',
    success:function(data){
          console.log(data);
      }
})

这是一个标准的jq的jsonp格式,这里我们没有给url加上callback但是依旧会成功,原因就是我们声明了期望返回的格式为‘jsonp’,然而实际上并没有这种格式,jsonp只是一个名称,一般来说他应该是一个js文件,或者是一个json格式的文件,而我们设置jsonp实际上是给jq用于判断该用什么方式来获取。

那么jq会先创建一个script元素,其src=xxx?key=xxx&com=xxx&no=xxx&callback=?,然后插入head中,加载完毕运行后再remove()删除;

为此我还特意去截了下图:

这里我们可以看到callback=?变成了callback=JQueryxxxxx跟了一大串字符,这个实际上就是jq自己随机创建的回调函数名,在没有指定回调函数名的情况下,JQueryxxxxx == success 里的函数,如果指定了,那就等于指定的函数。

自定义jsonp回调函数

比如我指定他的回调函数要是qiaolima,那么使用jsonpCallback属性

$.ajax({
     url:'xxx',
     type : 'GET',
     data:{
          key : "xxx",
          com : "xxx",
          no : "xx"
      },
    dataType : 'jsonp',
    success:function(data){
          console.log(data);
      },
    jsonpCallback : 'qiaolima'
})

//注意这个函数必须为全局函数,不能写在
//$(document).ready(function(){//不能写在这里}),
//而是要在外面
function qiaolima(data) {
  alert(data)
}

这里再讲一下callback=?的效果,jq会自动判断,将?替换为函数,如果没有指定回调函数名,会自己随机生成一个名称,就是刚刚JQueryxxxx这一大段,然后这个函数就是success设置的函数,意思如下:

var JQueryxxxxx = function(data){
      console.log(data);
}
//这个赋值的匿名函数就是success的值

深层解析

到了这里就可以进行更深层的剖析,先看个例子

$.ajax({
   url:'xxxx?callback=?',
      data:{
          key : "xxx",
          com : "xxx",
          no : "xxx"
      },
  dataType : 'json',
  success:function(data){
          console.log(data);
      }
})

我使用这段代码依旧可以跨域成功,为什么?

不同处之

  1. 之前讲的dataType用于给jq来判断是不是jsonp,但是这里我没有使用
  2. 我们给url加上了callback=?

原理 :

dataType 是用来告诉jq我即将返回的数据类型是什么,从而使用对应的方法,而json和jsonp实际上都是可以在script执行的文件类型,jq则会使用script元素去获取,如果我们指定为xml,这次jsonp就会失败,因为jq不会用带有src的元素去get获取了,不信可以自己试试,你会发现head不会插入script元素。

根据我的测试,当我们指定dataType 为:jsonp/json的时候会采用script元素作为载体。

使用script只是其中的一小步,更重要的则是callback=?,jq会自动判断,如果当前的url有callback=?这段文字,就是自动将?替换为正确的函数名,上面也说过,可以指定或者jq自己创建名称。

于是这两个条件都达到了,便可以跨域成功。

拓展思维

我们知道$.get(),$.post(),$.getJSON(),这三个方法是基于$.ajax()而来的,加上他们的第三个参数data同样是一个发送到服务器的数据,使用{}对象的形式,那么上面的方式我们还可以这样写

$.get({
    url:'xxx',
    data:{
        key : xxx,
        com : xxx,
        no : xxx
    },
    dataType:"jsonp",
    success:function(data){
        console.log(data);
    }
});

虽然jq规定第一个参数url是必须的,但是如果我们只传一个对象,对象里面加上url依旧可以成功运行。

在使用$.getJSON()方法实现上面的效果我们设置可以省略书写dataType。(因为该方法默认dataType:‘json’)

而$.getScript()虽然也是基于$.ajax,但是他是使用xhr对象作为载体,然后加载完毕后用eval()方法运行,所以你看不到有额外的script元素加入dom中去,为此无法适用jsonp,包括给dataType设置为script也是不行的,他们是相等的效果,除非你把dataType设置为json或者jsonp,那样就没有必要了,强行是不可取的。

以上就是关于jq的jsonp跨域的理解,有需要可以一起探讨。

  • weixiao kaixin tushetou jingkong deyi fanu liezui liuhan daku ganga bishi nanguo lihai qian yiwen numu tu yi haixiu se fadai minyan hehe henkaixin huaji biyiyan kuanghan maimeng shui xiaku penqi zhangzui pen aini ye niu laji ok chigua renshi kongbu shuai xiaoxiese touxiao huaixiao jingnu chihuai kaisang xiaoku koubi zhuangbi lianhong kanbujian shafa zhijing xiangjiao dabian yaowan redjing lazhu rizhi duocang chixigua hejiu xixi xiaopen goukun xiaobuchu shenme wusuowei guancha lajing chouyan xiaochi bie zhadanzui zhadanxiao

登录