[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