SNS平臺(tái)與第三方APP的JS通信實(shí)現(xiàn)
先看一下我們遇到了什么問(wèn)題?
在我們的白社會(huì)里,需要嵌入第三方應(yīng)用,而嵌入的方式是使用iframe,為了頁(yè)面美觀,這里就有一個(gè)最簡(jiǎn)單的需求:iframe的高度需要跟隨其本身內(nèi)容的變化而實(shí)時(shí)變化,這就要求主頁(yè)面根據(jù)iframe的內(nèi)容實(shí)時(shí)的去設(shè)置其樣式height值,但是因?yàn)榈谌綉?yīng)用和白社會(huì)不屬于同一個(gè)域,所以給實(shí)現(xiàn)帶來(lái)了一點(diǎn)小小的麻煩,所以才有以下的一些討論…
仔細(xì)分析一下問(wèn)題的實(shí)質(zhì)是什么呢?
其實(shí)這里需要解決的是,在一個(gè)頁(yè)面 A 中嵌入一個(gè)iframe 名字叫 B,A 和 B 不屬于同一個(gè)域,但是 A 和 B 需要進(jìn)行一些必要的通信,傳遞少量的數(shù)據(jù)信息,所以問(wèn)題的實(shí)質(zhì)就是主頁(yè)面與跨域 iframe 之間怎么通信,也就是怎么傳遞數(shù)據(jù)信息
下面就針對(duì)兩種不同的需求,總結(jié)一些比較簡(jiǎn)單,常用和穩(wěn)定的解決方案。
- 主頁(yè)面A 怎么向 iframe B 傳遞數(shù)據(jù)
- iframe B 怎么與 主頁(yè)面A 傳遞數(shù)據(jù)
需求一:主頁(yè)面A 與 iframe B 的怎么傳數(shù)據(jù)呢?
這種方式,是主頁(yè)面需要給 iframe B 傳遞數(shù)據(jù),然后 iframe B 獲得到數(shù)據(jù)后進(jìn)行特定的處理
實(shí)現(xiàn)方式
實(shí)現(xiàn)的技巧就是利用 location 對(duì)象的 hash 值,通過(guò)它傳遞通信數(shù)據(jù),我們只需要在主頁(yè)面A中設(shè)置 iframe B 的 src 后面多加個(gè) #data 字符串(data就是你要傳遞的數(shù)據(jù)),如下圖所示:
然后在 iframe B 中通過(guò)某種方式能即時(shí)的獲取到這兒 data 就可以了,其實(shí)常用的一種方式就是:
- 1. 在 iframe B 中通過(guò) setInterval 方法設(shè)置定時(shí)器, 監(jiān)聽(tīng) location.href 的變化即可獲得上面的 data 信息
- 2. 然后 iframe B 就能根據(jù)這個(gè) data 信息進(jìn)行相應(yīng)的邏輯處理
需求二:iframe B 與 主頁(yè)面A 的又怎么傳數(shù)據(jù)呢?
這種方式,是 iframe B 需要給主頁(yè)面?zhèn)鬟f數(shù)據(jù),然后主頁(yè)面根據(jù)獲得到數(shù)據(jù)后進(jìn)行特定的處理
實(shí)現(xiàn)方式
實(shí)現(xiàn)的技巧就是利用一個(gè)代理 IframeC,它嵌入到 iframe B 中,并且和主頁(yè)面A必須保持是同域,然后我們通過(guò)它充分利用上面第一種通信方式的實(shí)現(xiàn)原理就能把 iframe B 的數(shù)據(jù)傳遞給 iframeC,接下來(lái)的問(wèn)題就是怎么讓iframeC把數(shù)據(jù)傳遞給主頁(yè)面A ,如下圖所示:
因?yàn)?,iframeC 和主頁(yè)面是同域的,所以它們之間傳遞數(shù)據(jù)就變得簡(jiǎn)單多了,我們這里的方式就是使用一個(gè)經(jīng)常使用的屬性 window.top (也可以使用window.parent.parent),它返回對(duì)載入瀏覽器得最頂層 window 對(duì)象的引用,這樣我們就能直接條用主頁(yè)面A中方法啦,哈哈哈,簡(jiǎn)單吧。
到此,我們做個(gè)簡(jiǎn)單分析總結(jié)
當(dāng)然還有其他一些方式,也都測(cè)試過(guò),不是瀏覽器兼容性不好,就是實(shí)現(xiàn)起來(lái)復(fù)雜,通過(guò)以上方式就能很方便的在跨域的 iframe 和主頁(yè)面之間傳遞數(shù)據(jù)了,當(dāng)然也就能解決上面提到的設(shè)置 iframe 高度的問(wèn)題了,但是這種實(shí)現(xiàn)方式的前提也是最大的缺點(diǎn)就是 iframe 中的內(nèi)容必須是我們可控的,但是至少我們這種實(shí)現(xiàn)方式是建立在瀏覽器的安全規(guī)則之上的,沒(méi)有破壞應(yīng)用本身的安全性。
進(jìn)一步叨叨,實(shí)現(xiàn)時(shí)需要考慮的一些細(xì)節(jié)
上面的分析,其實(shí)只是一個(gè)簡(jiǎn)單的原理,在白社會(huì)里,雖然我們目前的需求還僅僅是實(shí)現(xiàn)第三方 iframe 形式的 App 的高度自適應(yīng),但是我們?cè)趯?shí)現(xiàn)的時(shí)候盡量考慮到了易用,可擴(kuò)展性和可維護(hù)性,比如:
- 讓第三方 App 只需加載一個(gè)我們提供的JS種子文件就能很方便的使用我們?yōu)槠涮峁┑母鞣N工具
- 上面的各種工具,我們采用包的形式進(jìn)行組織,最大化的實(shí)現(xiàn)按需加載
- 第一條中的JS種子文件只提供基礎(chǔ)的方法實(shí)現(xiàn),并且把最常用的工具包放在里面,比如高度自適應(yīng)
- 通過(guò)種子文件,我們還提供給第三方 App 一些常用的JS工具包,而且直接使用的類似YUI3模塊的動(dòng)態(tài)加載機(jī)制就可使用指定的工具包
- 對(duì)第三方 App 和 主頁(yè)面?zhèn)鬟f的數(shù)據(jù)進(jìn)行分類(自我調(diào)用,登錄驗(yàn)證,傳遞數(shù)據(jù)等等)
- 傳遞的數(shù)據(jù)使用滿足特定規(guī)范的JSON格式,并通過(guò)統(tǒng)一的服務(wù)出口發(fā)出去,主頁(yè)面提供一個(gè)統(tǒng)一服務(wù)接口解析數(shù)據(jù),并根據(jù)規(guī)范調(diào)用相應(yīng)的方法
- 還有,就是版本控制的問(wèn)題,為了盡量減少給第三方App帶來(lái)影響,以上所有這些JS文件的版本都是采用向后兼容的策略,小版本使用服務(wù)器設(shè)置SQUID緩存特定頻率的實(shí)效時(shí)間實(shí)現(xiàn),大版本更新根據(jù)用戶自己的需求手動(dòng)更改
- 當(dāng)然,以上可能不是最優(yōu)的解決方案,只是希望能給你一些幫助和引導(dǎo),我們也在逐步的改進(jìn)我們的一些實(shí)現(xiàn)方式,比如版本控制這塊兒,我們也有一些問(wèn)題需要解決
回過(guò)頭來(lái),我們?cè)倏袋c(diǎn)兒具體的代碼
主頁(yè)面A的源碼
- /*主頁(yè)面A*/
- <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN““http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd“>
- <html xmlns=“http://www.w3.org/1999/xhtml“>
- <head>
- <meta http-equiv=“Content-Type“ content=“text/html; charset=utf-8“ />
- <title>主頁(yè)面A</title>
- <script type=“text/javascript“ src=“/r/j-src/kola/core/kola.js“></script>
- <script type=“text/javascript“>
- function init(){
- document.domain = ‘bai.sohu.com’;
- alert(‘我是主框架,里面嵌入第三方應(yīng)用的IframeB,下面開(kāi)始加載第三方應(yīng)用’);
- var iframeTag = document.getElementById(‘frameB’),
- iframeSrc = ‘http://10.10.92.117/test/springwang/CrossDomain/autoSetHeight/iframePage.html’;
- iframeTag.src = iframeSrc;
- iframeTag.style.display = ‘block’;
- };
- function callback(h){
- var iframeB = document.getElementById(‘frameB’);
- alert(‘IframeC調(diào)用我(主框架)接口,把IframeB的高度傳給我,具體值是:’ + h);
- iframeB.style.height= h + 10 + ‘px’;
- iframeB.src += ‘#’+ h
- };
- </script>
- </head>
- <body onload=“init();“>
- <p>我是主頁(yè)框架,我的域是:bai.sohu.com</p>
- <iframe id=“frameB“ name=“frameB“ style=“display:none;“ src=“”></iframe>
- </body>
- </html>
iframeB(iframePage.html)的源碼
- <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN““http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd“>
- <html xmlns=“http://www.w3.org/1999/xhtml“>
- <head>
- <meta http-equiv=“Content-Type“ content=“text/html; charset=utf-8“ />
- <title>iframeB</title>
- <script type=“text/javascript“ src=“/r/j-src/kola/core/kola.js“></script>
- </head>
- <body onload=“init();“>
- <p style=“height:500px;background-color:#EEEEEE;“>我是三方應(yīng)用,我的域是:10.10.92.117</p>
- <iframe id=“frameC“ name=“frameC“ src=“”style=“height:1px;width:1px;display:none;“></iframe>
- </body>
- </html>
- <script type=“text/javascript“>
- function init(){
- alert(‘我是第三方應(yīng)用,下面開(kāi)始創(chuàng)建和主框架同域的通信通道IframC,并設(shè)置它的src,用#號(hào)傳遞高度值’);
- var iframeTag = document.getElementById(‘frameC’),
- iframeSrc = ‘http://bai.sohu.com/test/springwang/CrossDomain/autoSetHeight/iframePageC.html#’,
- pageHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
- iframeTag.src = iframeSrc + pageHeight;
- iframeTag.style.display = ‘block’;
- window.setTimeout(function(){
- alert(‘主頁(yè)面設(shè)置我(IframeB)的src,通過(guò)Hash(#)給我傳遞它收到的高度:’ + location.hash);
- },2000);
- };
- </script>
iframeC(iframePageC.html)的源碼
- <script type=“text/javascript“>
- document.domain = ‘bai.sohu.com‘;
- alert(‘我(IframeC)收到iframeB通過(guò)參數(shù)(#)給我傳遞高度值,我現(xiàn)在調(diào)用主頁(yè)面方法去設(shè)置IframB的高度‘);
- top.callback(window.location.href.split(‘#‘)[1]);
- </script>
完結(jié)了
希望以上簡(jiǎn)單的分析能給你當(dāng)前的項(xiàng)目有一些些幫助,大家多多拍磚啊~~~~~~~~~~~~~~~~~~~~最后一天上班,下周休假,哇哈哈,祝大家新春愉快~~
來(lái)源:http://ued.sohu.com/article/518
好東西