【知識點】關於iframe跨域通訊

語言: CN / TW / HK
ead>

theme: fancy

一、前言

中秋前的需求迭代,遇到一個iframe跨域通訊的問題,域名B的一個介面請求裡的欄位用到裡域名A下的賬戶id,原本的程式碼是直接用sessionStorage.getItem(info)的方式從域名A的快取中獲取使用者資訊,結果沒有拿到。

於是,我腦海裡回憶iframe相關的知識點,想到不同域名下的資訊是無法直接獲取的。沒錯,這個本應該熟悉的iframe基礎知識點,我靠幾分鐘的回憶才想起來。改完需求之後,我想我是時候重拾一下iframe的相關知識了。

以下關於iframe的介紹主要來自MDN

二、基礎資訊

掘金 ``` 點選掘金按鈕前展示: ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/461cbcc6603a495682d0b69a1523ade4~tplv-k3u1fbpfcp-watermark.image?) 點選掘金按鈕後展示: ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/db52c2d51e294b9c912698e16a0814c5~tplv-k3u1fbpfcp-watermark.image?) 在iframe視窗中開啟頁面 使用window.open()開啟視窗,該方法的第二個引數可以指定視窗名稱,我們通過window.name可以拿到iframe的name,賦值到window.open()中。 父頁面: ```js ``` **iframe頁面** ```js iframe-child
``` 可以在當前iframe視窗中開啟掘金的頁面: ![image.png](http://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0a30a78c511445e3b2d9f2ade9333dd1~tplv-k3u1fbpfcp-watermark.image?) ### 3.5 referrerpolicy 表示在獲取 iframe 資源時如何傳送 referrer 首部: > no-referrer: 不傳送 Referer 首部。 > > no-referrer-when-downgrade (default): 向不受 TLS (HTTPS) 保護的 origin 傳送請求時,不傳送 Referer 首部。 > > origin: referrer 首部中僅包含來源頁面的源。換言之,僅包含來源頁面的 scheme, host, 以及 port (en-US)。 > > origin-when-cross-origin: 發起跨域請求時,僅在 referrer 中包含來源頁面的源。發起同源請求時,仍然會在 referrer 中包含來源頁面在伺服器上的路徑資訊。 > > same-origin: 對於 same origin (同源)請求,傳送 referrer 首部,否則不傳送。 > > strict-origin: 僅當被請求頁面和來源頁面具有相同的協議安全等級時才傳送 referrer 首部(比如從採用 HTTPS 協議的頁面請求另一個採用 HTTPS 協議的頁面)。如果被請求頁面的協議安全等級較低,則不會發送 referrer 首部(比如從採用 HTTPS 協議的頁面請求採用 HTTP 協議的頁面)。 > > strict-origin-when-cross-origin: 當發起同源請求時,在 referrer 首部中包含完整的 URL。當被請求頁面與來源頁面不同源但是有相同協議安全等級時(比如 HTTPS→HTTPS),在 referrer 首部中僅包含來源頁面的源。當被請求頁面的協議安全等級較低時(比如 HTTPS→HTTP),不傳送 referrer 首部。 > > unsafe-url: 始終在 referrer 首部中包含源以及路徑 (但不包括 fragment,密碼,或使用者名稱)。這個值是不安全的, 因為這樣做會暴露受 TLS 保護的資源的源和路徑資訊。 **不新增referrerpolicy** ```js ``` iframe視窗中會展示src設定的掘金頁面: ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d2110b27bb1d48a7bed5a2806cdd4735~tplv-k3u1fbpfcp-watermark.image?) ### 3.8 srcdoc HTML5 only 該屬性是一段HTML程式碼,這些程式碼會被渲染到 iframe 中。如果瀏覽器不支援 srcdoc 屬性,則會渲染 src 屬性表示的內容。 ### 3.9 width 以CSS畫素格式HTML5,或以畫素格式HTML 4.01,或以百分比格式指定的 frame 的寬度。預設值是300。 ## 四、 指令碼 內聯的框架,就像 `` 元素一樣,會被包含在 window.frames 偽陣列(類陣列的物件)中。 有了 DOM HTMLIFrameElement 物件,指令碼可以通過contentWindow訪問內聯框架的window物件。 contentDocument屬性則引用了 `
``` **child.html** ```js iframe-child
改變背景顏色
``` **UI** ![image.png](http://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3db6d2b4da1f486585e1b2ca319ee557~tplv-k3u1fbpfcp-watermark.image?) 注:通過contentWindow改變內聯框架的屬性時,如果是本地頁面直接在Chrome中開啟,點選觸發更改事件的時候會報錯: ```js DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame. ``` 通過百度查詢資料瞭解到這是由於Chrome同源策略引起的,只有在頁面載入指令碼時才會出現該問題,所以我使用node啟動一個簡單的Web伺服器,通過本地服務再訪問頁面功能就可以了。貼出我的node.js程式碼: ```js // 目標: // 瀏覽器中輸入 http://127.0.0.1:3000/iframe/ 時,顯示 iframe.html //1. 載入 express 模組 const express = require('express'); //2. 建立伺服器 const app = express(); //3. 啟動伺服器 app.listen(3000, () => { console.log('express-server is running...'); }); const path = require('path'); // 4. 監聽路由 app.get('/iframe', (req, res) => { res.sendFile(path.join(__dirname, 'view', 'iframe.html'), err => {}); }); app.get('/child', (req, res) => { res.sendFile(path.join(__dirname, 'view', 'child.html'), err => {}); }); ``` 瀏覽器中輸入 [http://127.0.0.1:3000/iframe/](url),即可訪問本地頁面: ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/98ded5d8c7b047ba976c1784b31cae41~tplv-k3u1fbpfcp-watermark.image?) ### 4.2 框架內部指令碼可以通過window.parent訪問父視窗物件 框架內部指令碼可以通過window.parent訪問父視窗物件,進而改變父視窗的樣式 **iframe.html** ```js iframe
Iframe
父頁面box
``` **child.html** ```js iframe-child
``` **UI** ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1d28f3764dda4cfa8493f85faee54be2~tplv-k3u1fbpfcp-watermark.image?) ## 五、定位和縮放 作為一個可替換元素, 可以使用 object-position 和 object-fit 來定位、對齊、縮放 ``` **child.html** ```js iframe-child
``` UI ![image.png](http://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2feb56ff5e9d4a22999eb197aaa3145e~tplv-k3u1fbpfcp-watermark.image?) **2.postMessage** 先簡單瞭解一下postMessage的功能,來源[MDN](http://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage)。 window.postMessage() 方法可以安全地實現跨源通訊。 語法 ```js otherWindow.postMessage(message, targetOrigin, [transfer]); ``` > otherWindow > > 其他視窗的一個引用,比如iframe的contentWindow屬性、執行window.open返回的視窗物件、或者是命名過或數值索引的window.frames。 > > message > > 將要傳送到其他 window的資料。它將會被結構化克隆演算法序列化。這意味著你可以不受什麼限制的將資料物件安全的傳送給目標視窗而無需自己序列化。[1] > > targetOrigin > > 通過視窗的origin屬性來指定哪些視窗能接收到訊息事件,其值可以是字串"*"(表示無限制)或者一個URI。在傳送訊息的時候,如果目標視窗的協議、主機地址或埠這三者的任意一項不匹配targetOrigin提供的值,那麼訊息就不會被髮送;只有三者完全匹配,訊息才會被髮送。這個機制用來控制訊息可以傳送到哪些視窗;例如,當用postMessage傳送密碼時,這個引數就顯得尤為重要,必須保證它的值與這條包含密碼的資訊的預期接受者的origin屬性完全一致,來防止密碼被惡意的第三方截獲。如果你明確的知道訊息應該傳送到哪個視窗,那麼請始終提供一個有確切值的targetOrigin,而不是*。不提供確切的目標將導致資料洩露到任何對資料感興趣的惡意站點。 > > transfer 可選 > > 是一串和message 同時傳遞的 Transferable 物件. 這些物件的所有權將被轉移給訊息的接收方,而傳送一方將不再保有所有權。 **iframe.html** 父頁面通過postMessage向子頁面通訊,將targetOrigin設定為'*',不做同源限制。 ```js iframe
Iframe
``` **child.html** 子頁面使用window.addEventListener監聽父頁面message事件,回撥事件可以從event.data中獲取到父頁面傳遞的data。 ```js iframe-child
子頁面展示:
``` **UI** ![image.png](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/82bd56d28d024a22a9b54cecaa0e8cab~tplv-k3u1fbpfcp-watermark.image?) #### 6.2.2 子頁面向父頁面傳遞資料 子頁面通過也可以通過postMessage向父頁面傳遞資料,不同的是otherWindow為父頁面視窗,所以子頁面需要使用window.parent表示父頁面視窗(見4.2)。 **child.html** 子頁面通過postMessage向父頁面通訊,將targetOrigin設定為'*',不做同源限制。 ```js iframe-child
``` **iframe.html** 父頁面使用window.addEventListener監聽子頁面message事件,回撥事件可以從event.data中獲取到子頁面傳遞的data。 ```js iframe
Iframe
父頁面展示:

``` **UI** ![image.png](http://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2864099d0b35498c89c4ae97d50bbaa8~tplv-k3u1fbpfcp-watermark.image?) ## 七、總結 溫故而知新,可以為師矣。古人誠不欺我。 通過再次學習iframe,對iframe瞭解加深,且對於之前時常記憶不深的跨域解決方案有了更深刻的記憶。 時不我待,感謝不斷堅持的自己,加油!