[TOC]
### 前言
在多业务多平台的场景下,不可避免的会遇到业务整合或者页面整合的过程,而跨域通讯是必备的技能点,其中一个非常常见的点就是iframe自动根据载入页面高度撑大。其他方面需要跨域传值,保存页面基本信息等都离不开这项基本技术。
### 跨域概念
> 跨域是指从一个域名的网页去请求另一个域名的资源。比如从www.baidu.com 页面去请求 www.google.com 的资源。跨域的严格一点的定义是:只要 协议,域名,端口有任何一个的不同,就被当作是跨域。可以通过以下例子更好的理解跨域概念。
(限制跨域是浏览器的问题,不是js的问题)
| url |说明 | 是否允许通讯 |
| --- | --- | --- |
| http://www.a.com/a.js; http://www.a.com/b.js | 同一域名不同文件 | 允许 |
| http://www.a.com/js/a.js; http://www.a.com/script/b.js | 同一域名不同目录 | 允许 |
| http://www.a.com:8000/js/a.js; http://www.a.com/script/b.js | 同一域名,不同端口 | 不允许 |
| http://www.a.com/a.js;https://www.a.com/b.js | 同一域名,不同协议 | 不允许 |
| http://www.a.com/a.js;http://70.32.92.74/b.js | 域名和域名对应ip | 不允许 |
| http://www.a.com/a.js;http://script.a.com/b.js | 主域相同,子域不同 |不允许 |
| http://www.a.com/a.js;http://a.com/b.js | 同一域名,不同二级域名(同上)| 不允许 |
|http://www.cnblogs.com/a.js;http://www.a.com/b.js | 不同域名 | 不允许 |
* 备注:以下方案均以a.htm嵌入b.htm为例
### 方案一:document.domain+iframe
* 前提:主域相同,没有兼容问题
* 原理:设置为同一域名,改变识别标识
* 具体方案:
1 . 在www.a.com/a.html 中 :`document.domain = 'a.com';`
2 . 在www.script.a.com/b.html 中:`document.domain = 'a.com';`
### 方案二:动态创建script(可以忽略)
* 前提:无
* 原理:因为script标签不受同源策略的限制
* 具体方案:可以动态创建,也可以直接引入其他域名下的js文件,其中的js方法也可以直接使用。
`<script src="http://www.b.com/public.js"></script>`
附:js原生手写写法
~~~
function loadScript(url, func) {
var head = document.head || document.getElementByTagName('head')[0];
var script = document.createElement('script');
script.src = url;
script.onload = script.onreadystatechange = function(){
if(!this.readyState || this.readyState=='loaded' || this.readyState=='complete'){
func();
script.onload = script.onreadystatechange = null;}
};
head.insertBefore(script, 0);
}
window.baidu = { sug: function(data){console.log(data); }}
loadScript('http://suggestion.baidu.com/su?wd=w',function(){console.log('loaded')});
~~~
### 方案三 :window.name
* window.name 可以实现跨域存储,需要借助中介界面(`/proxy.html`);存储大小2M.
* 具体方案:
1. a.com/a.html
~~~
<script>
//需要传入传参的页面的url ,需要数据的页面或者iframeid (可以为null)
var getDomainData=function(url,contIframeId){
var state = 0,
iframe = document.createElement('iframe'),
loadfn = function() {
if(state === 1) {
var data = iframe.contentWindow.name; // 读取数据
console.log(data);
var jsonData=eval("("+data+")");
var jsonData=JSON.parse(data);
if(contIframeId)$("#"+contIframeId).height(data).show();
iframe.contentWindow.document.write('');//得到数据之后移除
iframe.contentWindow.close();
document.body.removeChild(iframe);
} else if(state === 0) {
state = 1;
iframe.contentWindow.location = "/proxy.html"; // 设置的代理文件
}
};
iframe.src = url; //需要传参的页面
iframe.style.display = "none";
if(iframe.attachEvent) {
iframe.attachEvent('onload', loadfn);
} else {
iframe.onload = loadfn;
}
document.body.appendChild(iframe);//载入iframe
}
getDomainData("http://www. b.com/b.html","contIframe");
</script>
~~~
2\. b.com/b.html :
~~~
var jsonData={
“name":value,
"name2":value2
}
window.name=JSON.stringify(jsonData);// 建议用json格式,也可以为字符串
~~~
###方案四:jsonp ,推荐方式
* 最为推荐的跨域解决方案,没有兼容问题
* 具体方案:
1. 前提:需要b.com准备一个响应的json文件或者数据,比如
~~~
inf({
"code":"ZJ2017",
"price":1788,
"tickets":100
});
~~~
1. 原生js方式
~~~
var script=document.createElement("script");
script.src="http://www.b.com/demo.json?callback=inf";
document.body.appendChild(script);
function inf(json){
console.log(json);
}
~~~
2. ajax :jsonp方式
~~~
$.ajax({
type:"get",
async:false,
url:"http://www.b.com/demo.json?callback=inf",
dataType:"jsonp",
jsonp:"callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
jsonpCallback:"inf",//为jsonp请求指定一个回调函数名
success:function(json){
console.log(json);
},
error:function(){
console.log("失败");
}
});
~~~
###方案五: location.hash + iframe
* 利用location.hash来进行传值,过程如下:
1.a.html首先创建自动创建一个隐藏的iframe,iframe的src指向b.com域名下的b.html页面
2.b.html响应请求后再将通过修改a.html的hash值来传递数据
3.同时在a.html上加一个定时器,隔一段时间来判断location.hash的值有没有变化,一旦有变化则获取获取hash值
注:由于两个页面不在同一个域下IE、Chrome不允许修改parent.location.hash的值,所以要借助于a.com域名下的一个代理iframe来解决这个问题(proxy.html)。
* 方案如下:
1. a.html 中
~~~
function startRequest(){
var ifr = document.createElement('iframe');
ifr.style.display = 'none';
ifr.src = 'http://test.promange.ucmed.cn';
document.body.appendChild(ifr);
}
function checkHash() {
try {
var data = location.hash ? location.hash.substring(1) : '';
if (console.log) {
console.log('Now the data is '+data);
}
} catch(e) {};
}
setInterval(checkHash, 2000);
~~~
2. b.html 中
~~~
switch(location.hash){
case '#data':
callBack("data");
break;
case '#paramset':
//do something……
break;
}
function callBack(dat){
try {
parent.location.hash = dat;
} catch (e) {
// ie、chrome的安全机制无法修改parent.location.hash,
// 所以要利用一个中间的a.com域下的代理iframe
var ifrproxy = document.createElement('iframe');
ifrproxy.style.display = 'none';
ifrproxy.src = 'http://www.a.com/proxy.html#'+dat; // 注意该文件在"a.com"域下
document.body.appendChild(ifrproxy);
}
}
~~~
3. a.com 的 proxy.html 中
`parent.parent.location.hash = self.location.hash.substring(1);`
### 方案六:html5 postMessage ,推荐使用,灵活方便
* 建议使用,兼容性,http://caniuse.com/#search=postMessage ;现代浏览器基本都支持,优点,只有发送时,接受一方才有数据,比较灵活。
* 具体方案:
1. a.html
~~~
window.addEventListener('message', function(event){
// 通过origin属性判断消息来源地址
if (event.origin == 'http://test.ybl.ucmed.cn') {
console.log(event.data);
}
}, false);
~~~
2. b.html
~~~
var json=({"height":300});//推荐json格式
var targetorigin="*";//发送目标服务器
top.postMessage(json,targetorigin) //设置为顶层窗口发送消息,也可以为window
~~~
### 方案七:websocket 实时通讯
* web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。(同源策略对web sockets不适用)
* web sockets原理:在JS创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。
* 具体方案:
1. a.html 创建发送请求
~~~
var ws = new WebSocket(“ws://localhost:8080”);
ws.onopen = function()
{
console.log(“open”);
ws.send(“hello”);
};
ws.onmessage = function(evt)
{
console.log(evt.data)
};
ws.onclose = function(evt)
{
console.log(“WebSocketClosed!”);
};
ws.onerror = function(evt)
{
console.log(“WebSocketError!”);
};
~~~
2. 需要服务器b.com 写对应的响应指令,不再赘述。
- 前端入门
- 前端入职须知
- 前端自我定位
- pc与手机页面差别
- 前端书单
- 前端种子计划
- 前端技术栈
- ps
- ps入门阶段
- html
- html入门
- html代码规范
- meta
- table
- iframe
- a标签详解
- image
- html代码审查工具
- h5专题
- h5入门
- h5新增属性
- canvas画布教程
- audio/video
- Geolocation
- Websockets
- Web storage
- Communication
- Web Workers
- requestAnimationFrame
- css
- css入门必学
- css代码规范
- 项目字体规范
- css基本位置布局
- css常见样式命名规则
- css代码优化建议
- css常用样式名
- css选择器攻略
- css盒子模型的理解
- css属性继承与默认值
- css代码审查工具
- css中常见的知识盲区
- css3新特性浅谈
- css新特性了解
- border-radius
- background
- transform
- animation
- white-space
- css常用技术
- 文本两端对齐
- css之浮动解决方案
- css优化建议
- 文本超出省略
- img-sprites
- rem布局教程
- 水平居中&垂直居中
- 固宽&变宽布局
- 宽高固定比例的盒模型
- 样式预处理语言
- less教程
- sass教程
- postcss教程
- js
- javascript入门
- js代码规范
- js基础拓展
- js代码审查工具
- js性能优化
- js基本语句
- 基本运算
- 基本语句语法
- js对象
- es6入门
- obj
- Array
- Date
- String
- Boolean
- Number
- Json
- RegExp
- Math
- function
- jquery入门
- jq核心思想
- jq基本语法
- jq插件库汇总
- js常用技术
- break&continue区别
- js对日期转换
- js控制运动-move.js
- 原生js-cookie语法
- ajax请求后回调
- 表单数据序列化
- zepto
- zepto入门
- 百度touchjs
- js编程
- 插件库
- 功能性插件
- pdfjs
- wdatepicker
- qrcoder
- barcode插件
- photoviewer
- hammer.js
- echarts
- 交互组件
- layerjs
- java
- java入门
- java基本语句
- springMVC
- javaweb
- vm模板引擎
- freemarker
- maven教程
- mySql教程
- flex教程
- flex入门
- git教程
- git入门
- git分支
- git-tag管理
- git注意事项
- git-torise入门
- ide-git插件使用
- web
- web兼容
- web兼容思想
- pc端兼容适配文档
- pc端兼容bug汇总
- ie兼容bug汇总
- 手机兼容bug汇总
- web安全
- jeecms
- web存储
- app/h5组件
- 安卓教程
- ios教程
- 前端教程
- rubikx的教程
- 其他
- artTemplate
- tmod使用
- 跨域问题
- markdown教程
- 常用工具
- postman-api调试
- web常识
- 浏览器ua统计
- ui框架
- easyui
- bootstrap
- 入门推荐
- weui
- sui-pc
- sui-mobile
- layerUi