亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JavaScript iframe 實(shí)現(xiàn)多窗口通信實(shí)例詳解

 更新時(shí)間:2022年10月08日 09:31:06   作者:代碼與野獸  
這篇文章主要為大家介紹了JavaScript iframe 實(shí)現(xiàn)多窗口通信實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

我最近在完善 easyjobs 代碼共享的功能。

左側(cè)是代碼編輯器,右側(cè)下方有一個(gè)控制臺(tái)。

當(dāng)我們?cè)谧髠?cè)編輯完成代碼后,點(diǎn)擊運(yùn)行 JS,右側(cè)的控制臺(tái)就可以輸出內(nèi)容。

而右側(cè)上方有一個(gè)渲染畫布,用來作為代碼運(yùn)行的容器。

你可以打開網(wǎng)址嘗試:www.easyjobs.biz/code-sharin…。

因?yàn)橥瑫r(shí)需要運(yùn)行 JavaScript 代碼,所以需要對(duì)環(huán)境進(jìn)行隔離。也就是要有一個(gè)獨(dú)立的 JavaScript 運(yùn)行環(huán)境,也可以叫做沙箱。

該怎么做呢?

實(shí)現(xiàn) JavaScript 沙箱的方案有很多,比如 iframe、with+Proxy、還有基于 Object.freeze 的不成熟提案,如果不涉及 Web API 的話,甚至可以借助 nodejs 的 vm 模塊。

不過 JavaScript 沙箱不是本文的重點(diǎn)。我的場(chǎng)景決定了 iframe 是最好的選擇,因?yàn)槲也粌H僅需要隔離 JS 代碼,還要隔離 HTML 和 CSS 代碼。

如何做沙箱呢?

iframe 有一個(gè) srcdoc 屬性,把要執(zhí)行的代碼傳給它就可以了。

<iframe srcdoc="<script>alert('hello')</script>"></iframe>

為了方便查看 iframe 中 console 輸出的內(nèi)容,我們還需要想辦法接收 iframe 傳遞過來的消息。

這也就是本文的主要內(nèi)容,iframe 通信實(shí)戰(zhàn)。

iframe 基本通信

我在這里用代碼來演示一下 iframe 最基本的通信是如何做的。

基本的 HTML 結(jié)構(gòu)

首先我們有一個(gè) index.html 文件。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>父窗口</title>
  </head>
  <body>
    <p>父窗口</p>
    <iframe src="./sub.html"></iframe>
    <button onclick="sendMessage()">發(fā)送一條消息給子窗口</button>
    <p id="response"></p>
  </body>
</html>

然后有一個(gè) sub.html。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>子窗口</title>
  </head>
  <body>
    <p>子窗口</p>
    <button onclick="sendMessage()">發(fā)送一條消息給父窗口</button>
    <p id="response"></p>
  </body>
</html>

它們的關(guān)系就是相互嵌套的關(guān)系。

打開 index.html,大概是下面這樣。

需要注意,多窗口通信需要使用 http(s) 協(xié)議。

使用 JavaScript 在窗口之間發(fā)送消息

我們來實(shí)現(xiàn)一下父窗口的 sendMessage 方法。

let sub = window.frames[0]
function sendMessage() {
  sub.postMessage({ msg: "來自父窗口的一條消息" })
}

其中 window.frames 是獲取當(dāng)前窗口的所有 iframe 元素,它返回一個(gè)類似數(shù)組的結(jié)構(gòu)。

通過調(diào)用 sub 的 postMessage 方法可以傳遞消息。

然后我們來到 sub.html 中編寫接收端的代碼。

const responseEl = document.getElementById("response")
window.addEventListener("message", function (e) {
  responseEl.innerHTML += `收到一條消息:${e.data.msg}`
})

接收端使用 window.addEventListener 來監(jiān)聽 message 事件。當(dāng)有其他窗口通過 poseMessage 來向當(dāng)前窗口發(fā)送消息時(shí),會(huì)觸發(fā)這個(gè)事件。

我們來點(diǎn)擊父窗口的「發(fā)送一條消息給子窗口」按鈕。

可以看到子窗口可以打印父窗口的消息。

同理,我們也可以通過 parent.postMessage 反向向父窗口傳遞消息。

在 sub.html 中繼續(xù)增加 sendMessage 代碼。

function sendMessage() {
  parent.postMessage({ msg: "來自子窗口的一條消息" })
}

這個(gè)代碼和 index.html 中發(fā)送消息的代碼很相似,唯一的區(qū)別就是接受者變成了 parent。parent 就是指當(dāng)前窗口的父窗口。

回到 index.html 中,增加監(jiān)聽代碼。監(jiān)聽代碼與子窗口完全一致,可以直接復(fù)制過來。

const responseEl = document.getElementById("response")
window.addEventListener("message", function (e) {
  responseEl.innerHTML += `收到一條消息:${e.data.msg}</br>`
})

我們來點(diǎn)擊子窗口的「發(fā)送一條消息給父窗口」按鈕。

這樣就實(shí)現(xiàn)了 iframe 窗口間雙向通信。

注意事項(xiàng)

類型

需要注意的是,postMessage 僅支持 JSON 支持的類型。

  • string
  • number
  • null
  • boolean
  • object
  • array

如果傳遞 undefined 的話,會(huì)自動(dòng)轉(zhuǎn)成 null。

除了上述類型以外的其他類型都不支持,比如 function、symbol。如果傳遞了這些類型,瀏覽器會(huì)報(bào)錯(cuò)。

如何傳遞函數(shù)并執(zhí)行

傳遞函數(shù)是一個(gè)很常見的需求,我們可以通過把函數(shù)轉(zhuǎn)換為字符串的方式進(jìn)行傳遞。

比如下面這樣:

function fn () {}
sub.postMessage({ fn: fn.toString() })

在接收方只需要通過 eval 就可以調(diào)用函數(shù)字符串了。

不過如果函數(shù)內(nèi)引用了外部變量的話,那就不行了。

比如下面這樣:

let name = '代碼與野獸'
function fn () {
  console.log(name)
}
sub.postMessage({ fn: fn.toString() })

因?yàn)榻邮斩藷o法獲取到發(fā)送端的變量。

如果碰巧接收端也存在 name 這個(gè)變量的話,eval 在執(zhí)行時(shí)就會(huì)訪問到接收端的變量而非發(fā)送端的變量。

這里也體現(xiàn)出了純函數(shù)的優(yōu)勢(shì)。如果我們遵循函數(shù)式編程范式編寫了純函數(shù),就不會(huì)導(dǎo)致這個(gè)問題。

如何在父窗口訪問到子窗口的 console

回到文章開頭,雖然我們可以通過 iframe 通信來傳遞消息,但實(shí)現(xiàn) iframe 執(zhí)行 console 同步到父窗口,仍然是個(gè)問題。

其實(shí)非常簡(jiǎn)單,把 console 對(duì)象上的所有方法劫持,然后把這段代碼加入到 iframe 最頂部就可以了。

var fns = new Map()
for(let key in console) {
  fns.set(key, console[key])
  console[key] = (...args) => {
    funcToString(args)
    window.parent.postMessage({ type: 'console.' + key, args }, "*")
    return fns.get(key)(...args)
  }
}

其中會(huì)調(diào)用 funcToString 方法,這個(gè)方法就是把所有的 function 字符串化。

因?yàn)槲覀儾淮_定傳入的結(jié)構(gòu)的嵌套深度,所以需要使用遞歸來轉(zhuǎn)換。

function funcToString(args) {
  Object.keys(args).forEach((key) => {
    const arg = args[key]
    if (typeof arg === "function") {
      args[key] = arg.toString()
    } else if (typeof arg === "object") {
      funcToString(arg)
    }
  })
}

以上就是JavaScript iframe 實(shí)現(xiàn)多窗口通信實(shí)例詳解的詳細(xì)內(nèi)容,更多關(guān)于JavaScript iframe多窗口通信的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論