深入理解iframe

本文并不是一篇iframe API文檔講解,因此想了解iframe API的同學(xué)請移步 MDN, 我將在現(xiàn)在瀏覽器的角度與大家取探討iframe, 因此,本文中雖然會(huì)提及一些iframe在舊瀏覽器中的應(yīng)用, 但并不會(huì)去講解。 所以,您對(duì)iframe在舊瀏覽器中的應(yīng)用場景感興趣的話,還請自己搜索相關(guān)資料。 同時(shí), 我也會(huì)從淺入深的來與大家探討iframe中的一些特性、各種現(xiàn)代瀏覽器中的渲染模式、應(yīng)用場景、以及在現(xiàn)代開發(fā)中的影響。

什么是iframe

在HTML中有三種結(jié)構(gòu)特征:樹結(jié)構(gòu)、層次結(jié)構(gòu)、框結(jié)構(gòu)。iframe正是框結(jié)構(gòu)中的一員。每個(gè)iframe中都是一個(gè)獨(dú)立的沙箱,它們擁有自己的window以及DOM。

為什么需要理解它

雖說在日常開發(fā)中,我們應(yīng)盡量少使用iframe,但在一些特殊場景下,我們也是不可避免需要使用iframe。因此,深入理解iframe能夠讓我們更合理的使用它。

渲染與阻塞

前面講到iframe是HTML三種結(jié)構(gòu)中的框結(jié)構(gòu),框結(jié)構(gòu)中還有另外兩個(gè)元素:framesetframe,但它們都已廢棄,不再推薦使用。
每一個(gè)框結(jié)構(gòu)都有一個(gè)獨(dú)立的HTML文檔,而不包含以上三種框結(jié)構(gòu)中任意一種的網(wǎng)頁就是最簡單的框結(jié)構(gòu)。其示圖如下:

frame.png

對(duì)應(yīng)的,復(fù)雜的框結(jié)構(gòu)即多個(gè)框結(jié)構(gòu)復(fù)合在一個(gè)頁面中, 其示圖如下:


frames.png

像上圖中的多框結(jié)構(gòu),非常不適合移動(dòng)端,因?yàn)檫@種結(jié)構(gòu)的頁面多觸控操作非常不友好。 到此,對(duì)于框結(jié)構(gòu)的基礎(chǔ)知識(shí)普及便告一段落了, 下面筆者將分別從 Chrome、Firefox、Safari、IE 11的測試結(jié)果來分析iframe在不同瀏覽器中的渲染模式以及阻塞情況,代碼如下:
我們先定義iframe要引用的頁面,并編寫如下代碼:

const start = Date.now();
const limit = function() {
return Date.now() - start;
}
while(limit() <= 1000 * 5) {}

接下來, 在主頁面中引入它:

<iframe src="./frame-sets.html"></iframe>

代碼很簡單, 就是讓iframe 阻塞至少5秒鐘,接下來分別在 Chrome、Firefox、Safari、IE11 中測試阻塞情況:

Chrome Firefox Safari IE11
阻塞主頁面渲染 false true true true
阻塞主頁面onload true true true true

從結(jié)果來看,阻塞onload并無異議,從來都是如此,但是驚訝的發(fā)現(xiàn)在Chrome中并不會(huì)阻塞主頁面的渲染, 我猜Chrome為iframe創(chuàng)建來一個(gè)單獨(dú)的沙箱進(jìn)程吧。

無阻塞加載iframe

前面講了iframe與阻塞,在不同的瀏覽器中表現(xiàn)大致相同(只有Chrome不會(huì)阻塞主頁面渲染,onload則都會(huì)受到阻塞)。在極大多數(shù)情況下,iframe都會(huì)阻塞主頁面的渲染, 所以我們急需采用一種不阻塞主頁面渲染的加載iframe的方式。那如果才能做到無阻塞加載iframe呢?思路有二:1. 直接使用setTimeout異步加載iframe;2. 在頁面觸發(fā)onload之后加載iframe。話不多說, 直接亮代碼:

// setTimeout 形式
setTimeout(function() {
frame.src = 'other-page-url';
}, 0);

這種方式十分簡潔, 但是需要注意的是, 如果你需要在頁面onload后執(zhí)行某些操作的時(shí)候, 需要在 setTimeout 回調(diào)中去綁定load函數(shù)。

window.addEventListener('load', function() {
iframe.src = 'other-page-url';
});

這種方式也是簡單粗暴,而且沒有setTimeout方式靈活, 沒辦法準(zhǔn)確到iframe加載完后, 在主頁面做一些操作。

iframe與跨域

跨域是我們開發(fā)過程中經(jīng)常遇到的問題,而如何解決跨域的問題, 網(wǎng)絡(luò)上已經(jīng)有非常多可行的方案, 至于最終選擇何種方案去處理, 還得結(jié)合實(shí)際業(yè)務(wù)場景選擇最合適的方案。接下來,我們將縮小解決方案的范圍, 只限定在iframe中去講解幾種跨域方案。
為了模擬跨域, 我們更改本地hosts。 以mac os 為例:
cd //
cd private/etc
vim hosts
添加如下代碼:

127.0.0.1    demo.com
127.0.0.1    cross.demo.com
127.0.0.1    other.com
  • 方案一:
    document.domain,這是瀏覽器暴露出來的一個(gè)準(zhǔn)只讀屬性(之所以說它是準(zhǔn)只讀屬性,是因?yàn)樗梢栽O(shè)置為當(dāng)前域名的超級(jí)域),利用這個(gè)特性,可以實(shí)現(xiàn)主域名相同子域名不同的網(wǎng)頁實(shí)現(xiàn)通信。代碼如下:
    main.html(http://demo.com:15100/main.html)
document.domain = 'demo.com';
window.alertFrameMsg = function(msg) {
alert(msg);
}
const frame = document.querySelector('#myFrame');
frame.src = 'http://cross.demo:15100/frame-sets.html';

frame-sets.html(http://cross.demo:15100/frame-sets.html)

document.domain = 'demo.com';
parent.alertFrameMsg('hello, world!');

如你所見, 只需要將兩者的document.domain設(shè)置為超域, 就可以實(shí)現(xiàn)主頁面與iframe的跨域通信了。而且相互之間的訪問非常自由(可以雙向通信)

  • 方案二:
    window.postMessage ,HTML5提供的API,可以安全的啟用跨域通信。語法非常簡單:targetWindow.postMessage(data, targetOrigin),第一個(gè)參數(shù)是要傳遞的數(shù)據(jù),令人高興的是將要發(fā)送到目標(biāo)window的數(shù)據(jù),會(huì)采用結(jié)構(gòu)克隆化算法序列化, 這意味著我們無需自己序列化,即可安全的傳輸數(shù)據(jù)對(duì)象到目標(biāo)window。如何在目標(biāo)窗口接收到數(shù)據(jù)呢?編寫如下代碼即可:
window.addEventListener('message', function(evt) {
console.log(evt.data);
}, false);

evt.data 即是 postMessage 中傳遞過來的數(shù)據(jù)! 結(jié)合上下文, 綜合起來:
main.html

window.frames['myFrame'].contentWindow.postMessage({name: 'injser', age: 18}, 'http://other-demo.com:15100');

frame-sets.html

window.addEventListener('message', function(evt) {
console.log(evt.data);
}, false);

這兩種跨域方式在iframe中驗(yàn)證都是可用的, 至于以前一些可用的方案,比如:location.hash、cross-fragment等, 在現(xiàn)代瀏覽器匯總基本驗(yàn)證已不可行。 因此并沒有在這里做出講解。
最后, 作為一名正在路上的前端er, 文章中難免有失誤的地方。 如有發(fā)現(xiàn), 歡迎勘誤!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容