深刻理解iframe

本文並非一篇iframe API文檔講解,所以想了解iframe API的同窗請移步 MDN, 我將在如今瀏覽器的角度與你們取探討iframe, 所以,本文中雖然會說起一些iframe在舊瀏覽器中的應用, 但並不會去講解。 因此,您對iframe在舊瀏覽器中的應用場景感興趣的話,還請本身搜索相關資料。 同時, 我也會從淺入深的來與你們探討iframe中的一些特性、各類現代瀏覽器中的渲染模式、應用場景、以及在現代開發中的影響。html

什麼是iframe

在HTML中有三種結構特徵:樹結構、層次結構、框結構。iframe正是框結構中的一員。每一個iframe中都是一個獨立的沙箱,它們擁有本身的window以及DOM。算法

爲何須要理解它

雖然說在平常開發中,咱們應儘可能少使用iframe,但在一些特殊場景下,咱們也是不可避免須要使用iframe。所以,深刻理解iframe可以讓咱們更合理的使用它。vim

渲染與阻塞

前面講到iframe是HTML三種結構中的框結構,框結構中還有另外兩個元素:framesetframe,但它們都已廢棄,再也不推薦使用。 
每個框結構都有一個獨立的HTML文檔,而不包含以上三種框結構中任意一種的網頁就是最簡單的框結構。其示圖以下: 
跨域

 


對應的,複雜的框結構即多個框結構複合在一個頁面中, 其示圖以下: 瀏覽器

 


像上圖中的多框結構,很是不適合移動端,由於這種結構的頁面多觸控操做很是不友好。 到此,對於框結構的基礎知識普及便告一段落了, 下面筆者將分別從 Chrome、Firefox、Safari、IE 11的測試結果來分析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 中測試阻塞狀況:dom

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

從結果來看,阻塞onload並沒有異議,歷來都是如此,可是驚訝的發如今Chrome中並不會阻塞主頁面的渲染, 我猜Chrome爲iframe建立來一個單獨的沙箱進程吧。異步

無阻塞加載iframe

前面講了iframe與阻塞,在不一樣的瀏覽器中表現大體相同(只有Chrome不會阻塞主頁面渲染,onload則都會受到阻塞)。在極大多數狀況下,iframe都會阻塞主頁面的渲染, 因此咱們急需採用一種不阻塞主頁面渲染的加載iframe的方式。那若是才能作到無阻塞加載iframe呢?思路有二:1. 直接使用setTimeout異步加載iframe;2. 在頁面觸發onload以後加載iframe。話很少說, 直接亮代碼:ide

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

 

這種方式十分簡潔, 可是須要注意的是, 若是你須要在頁面onload後執行某些操做的時候, 須要在 setTimeout 回調中去綁定load函數。

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

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

iframe與跨域

跨域 是咱們開發過程當中常常遇到的問題,而如何解決跨域的問題, 網絡上已經有很是多可行的方案, 至於最終選擇何種方案去處理, 還得結合實際業務場景選擇最合適的方案。接下來,咱們將縮小解決方案的範圍, 只限定在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,這是瀏覽器暴露出來的一個準只讀屬性(之因此說它是準只讀屬性,是由於它能夠設置爲當前域名的超級域),利用這個特性,能夠實現主域名相同子域名不一樣的網頁實現通訊。代碼以下: 
    main.html(http://demo.com:15100/main.html)
<script>
document.domain = 'demo.com';
window.alertFrameMsg = function(msg) {
alert(msg);
}
const frame = document.querySelector('#myFrame');
frame.src = 'http://cross.demo:15100/frame-sets.html';
</script>

 

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

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

 

如你所見, 只須要將二者的document.domain設置爲超域, 就能夠實現主頁面與iframe的跨域通訊了。並且相互之間的訪問很是自由(能夠雙向通訊)

  • 方案二: 
    window.postMessage ,HTML5提供的API,能夠安全的啓用跨域通訊。語法很是簡單:targetWindow.postMessage(data, targetOrigin),第一個參數是要傳遞的數據,使人高興的是將要發送到目標window的數據,會採用結構克隆化算法序列化, 這意味着咱們無需本身序列化,便可安全的傳輸數據對象到目標window。如何在目標窗口接收到數據呢?編寫以下代碼便可:
window.addEventListener('message', function(evt) {
console.log(evt.data);
}, false);

evt.data 便是 postMessage 中傳遞過來的數據! 結合上下文, 綜合起來: 
main.html(http://demo.com:15100/main.html)

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

frame-sets.html(http://other.com:15100/frame-sets.html)

<script>
window.addEventListener('message', function(evt) {
console.log(evt.data);
}, false);
</script>
相關文章
相關標籤/搜索