聊聊jsonp的原理及跨域

跨域是老生常谈的问题了也是面试会问到的,本质上就是浏览器同源策略javascript

浏览器同源策略

所谓同源策略是浏览器的一种安全策略,所谓同源是指,域名,协议,端口号彻底相同,可是开发者发现浏览器并不会对script标签的src仍是img标签的src,link标签的href限制,因此咱们能够利用这一特性能够进行ajax请求,有同窗可能会问到,我怎么去*证实浏览器对这些标签没有进行同源限制?php

请看下图:html

src或href连接的静态资源,本质上来讲也是一个get请求java

接下来咱们利用这个特性来解决ajax开发中遇到的跨域问题jquery

jsonp解决跨域

原理

在jquery开发年代,咱们一般是使用script标签引入对应的js文件,假设咱们如今有a,b两个文件,文件内的代码假设以下:git

a文件:github

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script type="text/javascript"> function getData(data){ console.log(data) } </script>
<script type="text/javascript" src="./b.js"></script>
</body>
</html>

复制代码

b文件:面试

getData({name:"vnues"})
复制代码

效果图:ajax

上面的代码很简单,我声明一个全局函数,传入一个data, b调用者调用getData方法,打印出data,那么咱们如今把调用者切换下,若是是 服务器调用这个方法,而且传入一串data数据,这样就拿到咱们所须要的数据了,实际上jsonp就是这个原理, 客户端声明好方法,将方法名传给后端,后端调用这个getData方法,实际咱们经过script标际加载进来的是一串脚本,就是后端建立的脚原本调用这个方法,因此jsonp的方法也 须要后端的支持,那么咱们来看看jsonp的实现吧

<script type="text/javascript"> 
    // 获得我的信息 
    let getUserData = function(data){
        console.log(data)
    }; 
    // 将方法名传给后端
    let url = "https://www.vnues.com/user.php?code=123&callback=getUserData"
    // 建立script标签,设置其属性 
    let script = document.createElement('script'); 
    script.setAttribute('src', url); 
    // 把script标签加入head,此时调用开始 
    document.getElementsByTagName('head')[0].appendChild(script); 
</script>
复制代码
// 数据
$data = [
    "name":"vnues",
    "age":"18",
    "like":"coding"
];
// 接收callback函数名称
$callback = $_GET['callback'];
// 输出
echo $callback . "(" . json_encode($data) . ")";
复制代码

拓展

github早已有很是丰富库支持jsonp方法,好比jsonp包,咱们能够经过Promise封装这个jsonpnpm

// npm install jsonp --save
import originJsonp from 'jsonp';
// Promise封装jsonp
export default function jsonp(url, data, option) {
 url += (url.indexOf('?') < 0 ? '?' : '') + param(data);
 return new Promise((resolve, reject) => {
   originJsonp(url, option, (err, data) => {
     if (!err) {
       resolve(data);
     } else {
       reject(err);
     };
   });
 });
};
// 将参数拼接到Url中
export function param(data) {
 let url = '';
 for (let k in data) {
   let value = data[k] !== undefined ? data[k] : '';
   url += `&${k}=${encodeURIComponent(value)}`;
 };
 return url.substr(1);
};
复制代码

总结

  • script标签src连接(地址)不必定是文件也能够是个api接口,只要返回一串调用脚本就能够实现jsonp

  • 实际上jsonp只支持get请求,并不支持post请求,因此这也是开发用的少的缘故