浏览器跨域解决方案

发布于 2022-06-16 14:30:06 字数 20790 浏览 1349 评论 0

什么是跨域

浏览器的同源策略会导致跨域,这里同源策略又分为以下两种 DOM 同源策略:

  • XmlHttpRequest 同源策略:禁止使用XHR对象向不同源的服务器地址发起 HTTP 请求。
  • 禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。

即不能通过 ajax 的方法去请求不同源中的文档、浏览器中不同域的框架之间是不能进行js的交互操作的。

只要协议、域名、端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作。

还有一点比较重要,为了安全,限制跨域是浏览器的行为,而不是 JS / 服务端的行为。你也可以自己开发一个浏览器,或者拿开源代码改,使得自己开发的浏览器能够跨域。

  1. 哪些情况属于跨域:
  • 协议不同,如 http, https;
  • 端口不同;
  • 主域相同,子域不同;(域名不同)
  • 主域不同;(域名不同)
  • ip 地址和域名之间也算是跨域,浏览器不会自动做 ip 域名的映射;
  1. 限制范围
  • Cookie、LocalStorage 和 IndexDB 无法读取。
  • DOM 无法获得。
  • AJAX 请求不能发送。
  1. 解决方案
  • jsonp
  • cors
  • WebSocket
  • 服务器代理转发
  • postMessage

AJAX请求不同源的跨域

一、 JSONP(浏览器与服务端的通信)

为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作 JSONP。该协议的一个要点就是通过创建一个 script 标签,将 src 设置为目标请求,插入到 dom 中,服务器接受该请求并返回数据,数据通常被包裹在回调钩子中

直接打开 index.html 文件浏览器端会报错。就是同源限制造成的。

// index.html
<!DOCTYPE html>
   <html lang="en">
   <head>
       <meta charset="UTF-8">
       <title>Document</title>
   </head>
   <body>
       
   </body>

    <script>
        function request(type, url, data) {
            const xhr = new XMLHttpRequest()
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
                        console.log(xhr.responseText)
                    } else {
                        console.log(xhr.status)
                    }
                }
            }
            xhr.open(type, url, true)
            xhr.send(data)
        }
        request('get', 'http://localhost:3000/user', null) // 网页默认端口 80 or 8000
    </script>
   </html>

1. JSONP 的实现原理(利用 Script 标签不受跨域限制而形成的一种方案)

JSONP 是 JSON with padding(填充式 JSON 或参数式 JSON)的简写,是应用 JSON 的一种方式。

JSONP 的实现原理很简单,利用 <script> 标签没有同源限制的特点,也就是 <script> 的src链接可以访问不同源的。不受同源限制的还有 <img><iframe><link>,对应这两个标签实现的跨域方法也有,比如图片 ping 等。(图片 ping:可以访问任何 url,一般用来进行点击追踪,做页面分析常用的方法。缺点:不能访问响应文本,只能监听是否响应)

下面为允许跨域资源嵌入的示例,即一些不受同源策略影响的标签示例:

  • <script src="..."></script> 标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。
  • <link rel="stylesheet" href="..."> 标签嵌入 CSS。由于 CSS 的松散的语法规则,CSS 的跨域需要一个设置正确的 Content-Type 消息头。不同浏览器有不同的限制: IE, Firefox, Chrome, Safari 和 Opera。
  • <img>嵌入图片。支持的图片格式包括 PNG、JPEG、GIF、BMP、SVG
  • <video><audio> 嵌入多媒体资源。
  • <object>, <embed><applet> 的插件。
  • @font-face 引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。
  • <frame> 和 <iframe> 载入的任何资源。站点可以使用 X-Frame-Options 消息头来阻止这种形式的跨域交互。

我们访问服务端,一般是获取存JSON数据,而JSONP则返回的是,包含函数的数据,将我们需要的 JSON 数据作为函数的参数。

即在客户端一般通过 <script> 标签的 src 访问带有 callback 查询参数的请求,来获取返回带有函数的数据,然后执行它,即可拿到这份数据(服务端注入的数据)完成跨域访问。

// 服务器返回的内容: 以字符串的形式返回
callback({"name": "xuthus"};

由于使用 script 标签的 src 属性,因此只支持get方法。

JSONP的请求过程

  • 请求阶段:浏览器创建一个 script 标签,并给其 src 赋值(同时在客户端注册一个 cb 方法 jsonpCallback(),放到 window 对象上;并把 callback 的名字( jsonpCallback )传给服务器:类似 http://example.com/api/?cb=jsonpCallback ),再将 script标签插入 DOM。
  • 发送请求:当给 scriptsrc 赋值并插入 DOM 后(不将 script标签插入 DOM ,是不会发起请求的),浏览器就会发起一个请求。
  • 数据响应:服务端将要返回的数据作为参数和函数名称拼接在一起(字符串的形式,格式类似"jsonpCallback({name: 'abc'})"返回。当浏览器接收到了响应数据,由于发起请求的是 script,所以相当于直接调用 jsonpCallback 方法,并且传入了一个参数。(解析 script 标签后,会执行 jsonpCallback(JOSN) )。

通过这种方式,即可实现跨域获取数据。

2. 封装一个jsonp工具

function createScript(url) {
    let script = document.createElement('script');
    script.setAttribute('src', url);
    script.setAttribute('type', 'text/javascript');
    script.async = true; // 或者直接插入文档底部 document.body.appendChild(script);
    return script;
}
// NumberObject.toString(radix) radix数字转化的基数:2~36之间的整数,36表示36进制,能将26个字母全部运用上
// /[^a-z]+/g :[^a-z]表示除a~z之间的字符 +:{1,}表示出现的所有字符
// 字符串的方法slice和substring都表示切割从起始位置到结束位置(不包括)之间的字符 substr()第一个参数表示切割的起始位置 第二个参数表示切割的长度
function gernerateCbName(prefix, num) {
    return prefix + Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, num);
}

// 我们将所有的callback都设置成了一个全局变量,这样的原因是因为我们需要在数据请求完成之后调用这个方法,因此不得不设置为一个全局变量。
// 但是当我们有多个请求,并且每个请求的处理都是不一样的时候,这个变量将会被覆盖。这是不行的,因此我们应该为每一次请求设置一个唯一且不会冲突的变量
// 页面中若有大量这样的请求之后,window中会含有大量的全局变量,而且还有大量的script标签,这显然不是我们想要的,所以我们要在请求完成之后删除变量和script标签。
function jsonp({ url, params, timerout = 0}) {
    let timer = null;
    let cbName = gernerateCbName('cb');
    let arr = [];
    params = { ...params, cbName }
    for(let key in params) {
        arr.push(`${key}=${params[key]}`)
    }

    let script = createScript(`${url}?${arr.join('&')}`);
    document.getElementsByTagName('head')[0].appendChild(script);

    // 错误处理(例如资源加载失败)
    script.onerror = function() {
        reject(new Error(`fetch ${url} failed`));
        window[cbName] = null;
        timer && clearTimeout(timer);
        document.getElementsByTagName('head')[0].removeChild(script);
    }

    return new Promise((resolve, reject) => {
        window[cbName] = function(data) {
            resolve(data);
            window[cbName] = null;
            timer && clearTimeout(timer);
            document.getElementsByTagName('head')[0].removeChild(script);
        }

        // 超时处理
        if(timerout != 0) {
            timer = setTimeout(() => {
                reject(new Error('TimeOut'));
                timer && clearTimeout(timer);
                // window[cbName] = null;
                // document.getElementsByTagName('head')[0].removeChild(script);
            }, timerout);
        }
    });
}

// 使用
jsonp({
    url: 'http://localhost:3000',
    params: {
        name: 'xql'
    },
    timerout: 1
}).then((data) => {
    console.log(data);
}).catch((err) => {
    console.log(err);
});

测试:node server.js 打开index.js 可以看到跨域成功

// server.js
let express = require('express');
let app = express();
app.get('/', function(req, res){
    let { cbName } = req.query; // cbName是和前端约定好的字段名称,即服务端和客户端这个字段保持一致
    console.log(cbName);
    res.end(`${cbName}('hello')`); // `${cbName}(数据 or JSON数据)`
});
app.listen(3000);

3. 注意

  • JSONP虽然可以兼容老版的浏览器,只支持GET请求而不支持POST等其它类型的HTTP请求;
  • 它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
  • 容易受xss攻击(安全问题)。在使用JSONP的时候必须要保证使用的JSONP服务必须是安全可信的。万一提供JSONP的服务存在页面注入漏洞,它返回的javascript都将被执行,若被注入这是非常危险的。
  • JSONP在调用失败的时候不会返回各种HTTP状态码(解决方法:添加timeout参数,虽然JSONP请求本身的错误没有被捕获,但是最终会因为超时而执行error回调)

二、CORS(通过前后端http header配置来进行跨域的一种方式)

Cross-Origin Resource Sharing 跨源资源共享

浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

可以把CORS分为:简单请求、复杂请求。

1. 简单请求

  • 只使用 GET, HEAD 或者 POST 请求方法。如果使用 POST 向服务器端传送数据,则数据类型(Content-Type)只能是 application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一种。
  • 不会使用自定义请求头(类似于 X-Modified 这种)。

流程:

  • 对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,自动增加一个Origin字段:表明本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
  • 如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含 Access-Control-Allow-Origin 字段,就知道出错了,从而抛出一个错误,被 XMLHttpRequest 的 onerror 回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。
    如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://api.bob.com: 该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
Access-Control-Allow-Credentials: true:该字段可选。它的值是一个布尔值且只有 true 这一个值,表示是否允许发送Cookie,同时开发者必须在AJAX请求中打开withCredentials: true属性才生效。默认情况下,Cookie不包括在CORS请求之中。

2. 非简单请求

2.1 预检请求

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。除了Origin字段,"预检"请求的头信息包括两个特殊字段。
Access-Control-Request-Method:该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,例如PUT。Access-Control-Request-Headers:该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,例如X-Custom-Header。

2.2 预检请求的回应

服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

  • Access-Control-Allow-Origin字段,表示该域名可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。

如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。

  • Access-Control-Allow-Methods: 该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
  • Access-Control-Allow-Headers:如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
  • Access-Control-Allow-Credentials:该字段与简单请求时的含义相同。
  • Access-Control-Max-Age:该字段可选,用来指定本次预检请求的有效期,单位为秒。例如:有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
2.3 浏览器的正常请求和回应

一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

优点

  • 前端方便不少,只需要发请求而不用考虑跨域问题;
  • 安全性能够得以控制和保障;

缺点

  • 兼容性不全面,需要做降级处理;

使用方式

  • 正常请求即可,无论是你要用xhr,还是用一些封装好的组件,如fetch,fetchJsonp,亦或是jquery一类的技术均可;
  • 后端在response时需要设置一定的配置参数,并保证安全策略

CORS和JSONP对比

  • JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
  • 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
  • JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS)。

完全不同源的跨域(两个页面之间的通信)

二、 postMessage

如果两个网页不同源,就无法拿到对方的DOM。典型的例子是 iframe 窗口和window.open方法打开的窗口,它们与父窗口无法通信。

比如,父窗口运行下面的命令,如果iframe窗口不是同源,就会报错。

// 父窗口想获取子窗口的DOM,因为跨源导致报错。
document.getElementById("myIFrame").contentWindow.document
// Uncaught DOMException: Blocked a frame from accessing a cross-origin frame.

反之亦然,子窗口获取主窗口的DOM也会报错。

window.parent.document.body
// 报错

如果两个窗口一级域名相同,只是二级域名不同,那么设置document.domain属性,就可以规避同源政策,拿到DOM。

对于完全不同源的网站,目前有三种方法,可以解决跨域窗口的通信问题。

    1. 利用 location.hash + iframe
    1. 利用 window.name + iframe
    1. 跨文档通信API: window.postMessage
1 通过修改document.domain来跨子域(只适用于主域相同、不同子域的框架间的交互)

有一个页面,它的地址是 http://www.example.com/a.html , 在这个页面里面有一个 iframe,它的src是 http://example.com/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的。

  • 在页面 http://www.example.com/a.html 中设置 document.domain:
<iframe id = "iframe" src="http://example.com/b.html" onload="test()"></iframe>
<script type="text/javascript">
    // function test(){
    //     var iframe = document.getElementById('ifame');
    //     var win = document.contentWindow;// undefined 可以获取到iframe里的window对象,但该window对象的属性和方法几乎是不可用的
    //     var doc = win.document;//这里获取不到iframe里的document对象
    //     var name = win.name;//这里同样获取不到window对象的name属性
    //     console.log(win)
    // }
    document.domain = 'example.com';//设置成主域
    function test(){
        alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 对象
    }
</script>
  • 在页面 http://example.com/b.html 中也设置document.domain:
<script type="text/javascript">
    document.domain = 'example.com';
    //在iframe载入这个页面也设置document.domain,使之与主页面的document.domain相同
</script>
2 利用 location.hash + iframe(这种跨域方法主要是通过设置/监听url的hash部分,来实现跨域)

原理:location.hash方式跨域,是子框架能够修改父框架src的hash值,子框架通过修改父框架 src 的 hash 值来传递信息。且更改hash值,页面不会刷新。但是传递的数据的字节数是有限的。

步骤:动态插入一个iframe,将iframe的src属性指向服务端地址。这时top window和包裹这个iframe的子窗口是不能通信的(同源策略),所以改变子窗口的路径就行了,将数据当做改变后的路径的hash值加在路径上,然后就能通信了(和window.name跨域几乎相同),将数据加在index页面地址的hash值上。index页面监听地址的hash值变化(html5有hashchange事件,用setInterval不断轮询判断兼容ie6/7),然后做出判断,处理数据。

假设域名a.com下的文件cs1.html要和jianshu.com域名下的cs2.html传递信息。
1、cs1.html首先创建自动创建一个隐藏的iframe,iframe的src指向jianshu.com域名下的cs2.html页面。
2、cs2.html响应请求后再将通过修改cs1.html的hash值来传递数据。
3、同时在cs1.html上加一个定时器,隔一段时间来判断 location.hash的值有没有变化,一旦有变化则获取获取hash值。

注:由于两个页面不在同一个域下IE、Chrome不允许修改parent.location.hash的值,所以要借助于a.com域名下的一个代理iframe。

//a页面的代码
<script type="text/javascript">
   iframe = document.createElement('iframe');
   iframe.style.display = 'none';
   var state = 0;

   iframe.onload = function() {
     if(state === 1) {
         var data = window.location.hash;
         console.log(data);
         iframe.contentWindow.document.write('');
         iframe.contentWindow.close();
         document.body.removeChild(iframe);
     } else if(state === 0) {
         state = 1;
         iframe.contentWindow.location = 'http://localhost: 4000/b.html';
     }
   };
   document.body.appendChild(iframe);
</script>
//b页面代码
<script type="text/javascript">
   parent.location.hash = "world";
</script>

优点:1.可以解决域名完全不同的跨域。2.可以实现双向通讯。
缺点:location.hash会直接暴露在URL里,并且在一些浏览器里会产生历史记录,数据安全性不高也影响用户体验。另外由于URL大小的限制,支持传递的数据量也不大。有些浏览器不支持 onhashchange 事件,需要轮询来获知URL的变化。

3 利用 window.name + iframe (父框架和子框架的src必须指向同一域名)

原理:window.name(一般在js代码里出现)的值不是一个普通的全局变量,而是当前窗口的名字,这里要注意的是每个iframe都有包裹它的window,而这个window是top window的子窗口,每个 iframe 都有 window.name 的属性。window.name属性的神奇之处在于一个 iframe 的 name 值在不同的页面(甚至不同域名,即改变了这个iframe的 src 默认情况下,name依然保持不变)加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的 name 值(2MB)

window.name = data;//父窗口先打开一个子窗口,载入一个不同源的网页,该网页将信息写入。        
 location = 'http://parent.url.com/xxx.html';//接着,子窗口跳回一个与主窗口同域的网址。
 var data = document.getElementById('myFrame').contentWindow.name。//然后,主窗口就可以读取子窗口的window.name了。

步骤:在iframe载入过程中,迅速重置iframe.src的指向,使之与index.html同源,那么index页面就能去获取到这个 iframe 的name值了(把子框架的 src 改变和父框架同源,而子框架的 name 依然保持不变)!

所以,iframe子框架需要一直不停地刷新,每次触发onload事件后,重置src,相当于重新载入页面,又触发onload事件,于是就不停地刷新了(但是需要的数据还是能输出的);

// index.html
  <script type="text/javascript"> 
    iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    iframe.src = 'http://localhost: 4000/b.html';
    document.body.appendChild(iframe);
    var state = 0;
    
    iframe.onload = function() {
      if(state === 1) {
          var data = JSON.parse(iframe.contentWindow.name);
          console.log(data);
          iframe.contentWindow.document.write('');
          iframe.contentWindow.close();
        document.body.removeChild(iframe);
      } else if(state === 0) {
          state = 1;
          iframe.contentWindow.location = 'http://localhost: 8000/proxy.html'; // 父框架的域
      }
    }; 
  </script>
// http://localhost: 4000/b.html文件
<script type="text/javascript">
   window.name = "hello";
</script>

优点:window.name容量很大,可以放置非常长的字符串;(2MB)
缺点:必须监听子窗口window.name属性的变化,影响网页性能。

4 window.postMessage

信息传递除了客户端与服务器之前的传递,还存在以下几个问题:

  • 页面和新开的窗口的数据交互。
  • 多窗口之间的数据交互。
  • 页面与所嵌套的iframe之间的信息传递。

上面两种方法都属于破解,再加上iframe用的比较少了,所以这些方法也就有点过时了。window.postMessage是一个HTML5的api,允许两个窗口之间进行跨域发送消息,不论这两个窗口是否同源。这个应该就是以后解决dom跨域通用方法了。

// 语法:data:将要发送到其他 window 的数据; targetOrigin:指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI
otherWindow.postMessage(data, targetOrigin, [transfer]);

父窗口和子窗口都可以通过message事件,监听对方的消息。message事件的事件对象event,提供以下三个属性:

  • event.source:发送消息的窗口。
  • event.origin: 消息发向的网址。
  • event.data:消息内容。
// a.html
<iframe src="http://localhost:4000/b.html" id="frame" onload="load()"></iframe>
<script>
function load(params){
    let frame = document.getElementById('frame');
    //获取iframe中的窗口,给iframe里嵌入的window发消息
    frame.contentWindow.postMessage('hello','http://localhost:4000')
    // 接收b.html回过来的消息
    window.onmessage = function(e){
        console.log(e.data)
    }
}
</script>
// b.html
<script>
//监听a.html发来的消息
window.onmessage = function(e){
    console.log(e.data)
    //给发送源回消息
    e.source.postMessage('nice to meet you',e.origin)
}
</script>
/*
 * A窗口的域名是<http://example.com:8080>,以下是A窗口的script标签下的代码:
 */

var popup = window.open(...popup details...);

// 如果弹出框没有被阻止且加载完成

// 这行语句没有发送信息出去,即使假设当前页面没有改变location(因为targetOrigin设置不对)
popup.postMessage("The user is 'bob' and the password is 'secret'",
                  "https://secure.example.net");

// 假设当前页面没有改变location,这条语句会成功添加message到发送队列中去(targetOrigin设置对了)
popup.postMessage("hello there!", "http://example.org");

function receiveMessage(event)
{
  // 我们能相信信息的发送者吗?  (也许这个发送者和我们最初打开的不是同一个页面).
  if (event.origin !== "http://example.org")
    return;

  // event.source 是我们通过window.open打开的弹出页面 popup
  // event.data 是 popup发送给当前页面的消息 "hi there yourself!  the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);
/*
 * 弹出页 popup 域名是<http://example.org>,以下是script标签中的代码:
 */

//当A页面postMessage被调用后,这个function被addEventListenner调用
function receiveMessage(event)
{
  // 我们能信任信息来源吗?
  if (event.origin !== "http://example.com:8080")
    return;

  // event.source 就当前弹出页的来源页面
  // event.data 是 "hello there!"

  // 假设你已经验证了所受到信息的origin (任何时候你都应该这样做), 一个很方便的方式就是把enent.source
  // 作为回信的对象,并且把event.origin作为targetOrigin
  event.source.postMessage("hi there yourself!  the secret response " +
                           "is: rheeeeet!",
                           event.origin);
}

window.addEventListener("message", receiveMessage, false);

优点:不需要后端介入就可以非常简单的的做到跨域,一个函数外加两个参数(请求url,发送数据)就可以搞定;移动端兼容性好;
缺点

  • 无法做到一对一的传递方式:监听中需要做很多消息的识别,由于postMessage发出的消息对于同一个页面的不同功能相当于一个广播的过程,该页面的所有onmessage都会收到,所以需要做消息的判断;
  • 安全性问题:三方可以通过截获,注入html或者脚本的形式监听到消息,从而能够做到篡改的效果,所以在 postMessag e和 onmessage 中一定要做好这方面的限制;
  • 发送的数据会通过结构化克隆算法进行序列化,所以只有满足该算法要求的参数才能够被解析,否则会报错,如function就不能当作参数进行传递;

LocalStorage

文章开头说了,浏览器的同源策略会导致,LocalStorage有同源限制。

解决办法:通过window.postMessage,读写其他窗口的 LocalStorage 也成为了可能。

References

动手实现一个JSONP
前端跨域问题及解决方案
跨域资源共享 CORS 详解

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84960 人气
更多

推荐作者

lorenzathorton8

文章 0 评论 0

Zero

文章 0 评论 0

萧瑟寒风

文章 0 评论 0

mylayout

文章 0 评论 0

tkewei

文章 0 评论 0

17818769742

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文