# 解决跨域问题

# 服务器设置

CORS 需要服务器设置header :Access-Control-Allow-Origin。

# jsonp解决跨域

利用script标签的src属性不受同源策略的限制,向服务端发送一个带有callback参数的请求,服务端接收到请求后把数据当做callback的参数返回这个方法。这个方法要在请求前就要定义好,这个方法接受一个参数,用来返回数据。请求后就自动调用了。

这个callback的值(方法名)要事先约定好。

只支持GET请求。

function jsonp(getUrl) {
  var script = document.createElement('script');
  script.src = getUrl + '?callback=fn';
  document.body.appendChild(script);
}

// 定义好方法获取数据
function fn (data) {
  console.log(data)
}

jsonp(getUrl)

服务端

// 后端接收到请求后把数据传入这个函数并返回这个
var data = {}
fn(data)

# nginx反向代理

当前域名下请求另一个域名下的资源或者接口,肯定会跨域,无法请求,可以通过nginx代理到另一个域名的接口。

通过配置nginx.conf文件下server的proxy_pass来实现代理。

比如要请求另一个域名的api http://www.b.com/api/user?name=xxx&age=xxx

  server {
    listen       8080; // 监听当前域名断开
    server_name  http://www.a.com; // 监听当前域名,本地启动项目为localhost或者ip地址

    location / {
        root   C:/GitProject/tourbox;
        index  index.html index.htm;
    }

    // 监听某个api 然后代理到另一个域名下的api 或者全部代理
    // 请求http://www.a.com/api/user?name=&age=会被代理到http://www.b.com/api/user?name=&age=
    location /api {
        proxy_pass  http://www.b.com/api; 
    }
  }

# postMessage跨域

postMessage可以允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。

otherWindow.postMessage(message, targetOrigin, [transfer])

otherWindow:其他窗口的一个引用,比如 iframe 的 contentWindow 属性、执行 window.open 返回的窗口对象、或者是命名过或数值索引的 window.frames。
message: 将要发送到其他 window 的数据。
targetOrigin: 通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个 URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。
transfer(可选):是一串和 message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

父页面:

<body>
  <h1>我是父页面</h1>
  <p><button id="btn">发送子页面消息</button></p>

  <iframe id="iframe" src="http://localhost:3009/" frameborder="0"></iframe>

  <script>
    var iframe = document.getElementById('iframe');
    var btn = document.getElementById('btn');
    // 像子页面发送数据
    btn.onclick = function() {
      iframe.contentWindow.postMessage('父页面数据', '*'); // * 代表所有都可以接收
    }

    // 接收子页面发送的数据
    window.addEventListener("message", receiveSubMsg, false);
    function receiveSubMsg (event) {
      console.log(event.data); // 子页面数据
    }
  </script>
</body>

子页面:

<body>
  
  <h1>我是子页面</h1>

  <script>
    // 监听message
    window.addEventListener("message", receiveMsg, false);
    function receiveMsg (event) {
      console.log(event.data); // 父页面数据
    }

    // 像父页面发送数据
    var btn = document.getElementById('btn');
    btn.onclick = function() {
      window.parent.postMessage('父页面数据', '*'); // 通过window.parent像父页面发送数据
    }
  </script>
</body>

# 使用window.name + iframe

window.name有一个奇妙的性质,如果当前页面设置了window.name,在不关闭当前页面的情况下,即使使用window.location.href = 'xxx',进行跳转到其他页面window.name也还会保留。

所以可以结合iframe跳转到设置了window.name不同源的页面,然后再跳转到同源下的空白页面,这个空白页面会保留不同源页面设置的window.name,最后获取这个同源下空白页面的window.name就可以解决跨域的问题了。

http://www.a.com

index.html
empty.html

http://www.b.com

index.html

http://www.a.com/index.html

<body>
  <h1>我是父页面</h1>

  <!-- 第一次跳转到不同源页面 -->
  <iframe id="iframe" src="http://www.b.com/index.html" frameborder="0" onload="load()"></iframe>

  <script>
    var iframe = document.getElementById('iframe')

    var first = true

    function load() {
      // 第一次加载后设置了window.name后跳转到同源empty空页面下
      if (first) {
        iframe.src = 'http://www.a.com/empty.html'
        first = false
      } else {
        // 第二次跳转到同源empty空页面下获取当前iframe页面的name属性
        console.log(iframe.contentWindow.name) // hhhh
      }
    }
  </script>
</body>

http://www.b.com/index.html

<body>
  <h1>我是不同源页面</h1>

  <script>
    // 设置window.name
    window.name = 'hhhh'
  </script>
</body>

# location.hash + iframe

还是通过另一个同源页面做中转

实现原理:当前页面可以通过iframe跳转给不同源页面传递一个hash值,不同源页面通过也通过iframe跳转到和当前同源的空页面传递一个hash值,然后在这个同源的空页面把hash值传给当前页面。

http://www.a.com/index.html

<body>
  <h1>我是父页面</h1>

  <iframe id="iframe" src="http://localhost:3009/index.html#changzhen" frameborder="0" ></iframe>

  <script>
    window.onhashchange = function () {
      // 检测hash的变化
      console.log(456, location.hash) // #monkey
    }
  </script>
</body>

http://www.b.com/empth.html

<body>
  <h1>empty.html</h1>

  <script>
    // 同源空页面将结果传给父页面的hash值中,两次iframe跳转可通过parent.parent访问父页面
    window.parent.parent.location.hash = location.hash
  </script>
</body>

http://www.b.com/index.html

<body>
  
  <h1>我是不同源页面</h1>

  <script>
    console.log(location.hash) //  #changzhen
    // 通过iframe再跳转到另一个同源空页面,把hash值传给它
    var iframe = document.createElement('iframe')
    iframe.src = 'http://localhost:3008/empty.html#monkey'
    document.body.appendChild(iframe)
  </script>
</body>

# document.domain + iframe

这种方式只能用于二级域名相同的情况下。

比如 a.test.com 和 b.test.com 就属于二级域名,它们都是 test.com 的子域名

只需要给页面添加 document.domain ='test.com' 表示二级域名都相同就可以实现跨域。

a.test.com/index.html

<body>
  <h1>当前二级域名页面</h1>

  <iframe id="iframe" src="http://b.test.com:3000/index.html" frameborder="0" onload="load()" ></iframe>

  <script>
    // 设置document.domain为主域名,都表示二级域名
    document.domain = 'test.com'
    function load() {
      console.log(iframe.contentWindow.a) // 10
    }
  </script>
</body>

b.test.com/index.html

<body>
  <h1>另一个二级域名页面</h1>

  <script>
    // 设置document.domain为主域名,都表示二级域名
    document.domain = 'test.com'
    var a = 10
  </script>
</body>