使用postMessage進行react和iframe的數據通信.md


將react的數據傳遞給iframe

1.首先在父組件(react文件)內引入iframe

<iframe 
    style={{border:0,width:"100%",height:"100%",}}
    src='/model.html'
    id='calculation'
    //onLoad={this.sendToken}
/>

2.然后在父組件(react文件)內的Button按鈕上綁定點擊事件

<Button type="primary" onClick={this.handleClick.bind(this)}>點我</Button>

3.繼續在父組件(react文件)內編寫handleClick方法,使用postMessage傳遞數據

handleClick = () =>{
    //必須是iframe加載完成后才可以向子域發送數據
    const childFrameObj = document.getElementById('calculation');
    childFrameObj.contentWindow.postMessage(1233, '*'); //window.postMessage
};

4.在iframe頁面編寫回掉函數並監聽message事件

//回調函數
function receiveMessageFromIndex ( event ) {
    console.log( '我是iframe,我接受到了:', event.data );
}

//監聽message事件
window.addEventListener("message", receiveMessageFromIndex, false);

將iframe的數據傳遞給react並渲染到頁面上

1.在iframe頁面編寫點擊事件,使用postMessage傳遞數據

<div id="click">點我</div>
<script>
    document.getElementById('click').onclick=function(){
        parent.postMessage('來自iframe的信息','*'); //window.postMessage
    }
</script>

2.在父組件(react文件)的componentDidMount方法中編寫接收代碼

constructor(){
    super();
    this.state = {
        modelId : ''    //1.初始化一個接收數據的變量
    };
};
componentDidMount(){    //問題延申:React數據獲取為什么一定要在componentDidMount里面調用?
    let self = this;    //為了避免作用域及緩存
    window.receiveMessageFromIndex = function ( event ) {
        if(event!=undefined){
            console.log( '我是react,我接受到了來自iframe的模型ID:', event.data );
            self.setState({
                modelId:event.data  //2.給變量賦值
            })
        }
    }
    //監聽message事件
    window.addEventListener("message", receiveMessageFromIndex, false);
};

3.將iframe傳遞過來的數據渲染到組件

<p>來自iframe的模型ID:{this.state.modelId}</p>

到目前為止reactiframe的通信就告一段落了。

問題延申:React數據獲取為什么一定要在componentDidMount里面調用?

1.constructor中獲取數據的話,如果時間太長,或者出錯,組件就渲染不出來,整個頁面都沒法渲染了。

2.constructor是作組件state初紿化工作,並不是設計來作加載數據的。

3.如果使用SSR(服務端渲染),componentWillMount會執行2次,一次在服務端,一次在客戶端。而componentDidMount不會。

4.constructor可以完成state初始化,componentWillMount使用的很少,目前16版本加入了UNSAFE來標識componentWillMount,新的生命周期static getDerivedStateFromProps()也會替代這個。

5.React16之后采用了Fiber架構,只有componentDidMount聲明周期函數是確定被執行一次的,類似ComponentWillMount的生命周期鈎子都有可能執行多次,所以不加以在這些生命周期中做有副作用的操作,比如請求數據之類。

6.componentDidMount方法中的代碼,是在組件已經完全掛載到網頁上才會調用被執行,所以可以保證數據的加載。此外,在這方法中調用setState方法,會觸發重渲染。所以,官方設計這個方法就是用來加載外部數據用的,或處理其他的副作用代碼。


接下來介紹下 postMessage 這個方法 MDN (需要梯子)

  • window.postMessage() 方法可以安全地實現跨源通信。通常,對於兩個不同頁面的腳本,只有當執行它們的頁面位於具有相同的協議(通常為https),端口號(443為https的默認值),以及主機 (兩個頁面的模數 Document.domain設置為相同的值) 時,這兩個腳本才能相互通信。window.postMessage() 方法提供了一種受控機制來規避此限制,只要正確的使用,這種方法就很安全。

  • window.postMessage() 方法被調用時,會在所有頁面腳本執行完畢之后(e.g., 在該方法之后設置的事件、之前設置的timeout 事件,etc.)向目標窗口派發一個 MessageEvent 消息。 該MessageEvent消息有四個屬性需要注意: message 屬性表示該message 的類型; data 屬性為 window.postMessage 的第一個參數;origin 屬性表示調用window.postMessage() 方法時調用頁面的當前狀態; source 屬性記錄調用 window.postMessage() 方法的窗口信息。

語法

otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow
其他窗口的一個引用,比如iframe的contentWindow屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames。

message
將要發送到其他 window的數據。它將會被結構化克隆算法序列化。這意味着你可以不受什么限制的將數據對象安全的傳送給目標窗口而無需自己序列化。

targetOrigin
通過窗口的origin屬性來指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示無限制)或者一個URI。在發送消息的時候,如果目標窗口的協議、主機地址或端口這三者的任意一項不匹配targetOrigin提供的值,那么消息就不會被發送;只有三者完全匹配,消息才會被發送。這個機制用來控制消息可以發送到哪些窗口;例如,當用postMessage傳送密碼時,這個參數就顯得尤為重要,必須保證它的值與這條包含密碼的信息的預期接受者的origin屬性完全一致,來防止密碼被惡意的第三方截獲。如果你明確的知道消息應該發送到哪個窗口,那么請始終提供一個有確切值的targetOrigin,而不是*。不提供確切的目標將導致數據泄露到任何對數據感興趣的惡意站點。

transfer 可選
是一串和message 同時傳遞的 Transferable 對象. 這些對象的所有權將被轉移給消息的接收方,而發送一方將不再保有所有權。

message 的屬性有:

data

從其他 window 中傳遞過來的對象。

origin

調用 postMessage 時消息發送方窗口的 origin . 這個字符串由 協議、“😕/“、域名、“ : 端口號”拼接而成。例如 https://example.org (隱含端口 443)http://example.net (隱含端口 80)http://example.com:8080。請注意,這個origin不能保證是該窗口的當前或未來origin,因為postMessage被調用后可能被導航到不同的位置。

source

對發送消息的窗口對象的引用; 您可以使用此來在具有不同origin的兩個窗口之間建立雙向通信。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM