跨頁(yè)面通信的各種姿勢(shì)

將跨頁(yè)面通訊類比計(jì)算機(jī)進(jìn)程間的通訊,其實(shí)方法無(wú)外乎那么幾種捡需,而web領(lǐng)域可以實(shí)現(xiàn)的技術(shù)方案主要是類似于以下兩種原理:

  • 獲取句柄办桨,定向通訊
  • 共享內(nèi)存,結(jié)合輪詢或者事件通知來(lái)完成業(yè)務(wù)邏輯

由于第二種原理更利于解耦業(yè)務(wù)邏輯站辉,具體的實(shí)現(xiàn)方案比較多樣呢撞。以下是具體的實(shí)現(xiàn)方案,簡(jiǎn)單介紹下饰剥,權(quán)當(dāng)科普:

獲取句柄

具體方案

父頁(yè)面通過(guò)window.open(url, name)方式打開的子頁(yè)面可以獲取句柄殊霞,然后通過(guò)postMessage完成通訊需求。

// parent.html
const childPage = window.open('child.html', 'child')

childPage.onload = () => {
    childPage.postMessage('hello', location.origin)
}

// child.html
window.onmessage = evt => {
    // evt.data
}

tips

  1. 當(dāng)指定window.open的第二個(gè)name參數(shù)時(shí)汰蓉,再次調(diào)用window.open('****', 'child')會(huì)使之前已經(jīng)打開的同name子頁(yè)面刷新
  2. 由于安全策略绷蹲,異步請(qǐng)求之后再調(diào)用window.open會(huì)被瀏覽器阻止,不過(guò)可以通過(guò)句柄設(shè)置子頁(yè)面的url即可實(shí)現(xiàn)類似效果
// 首先先開一個(gè)空白頁(yè)
const tab = window.open('about:blank')

// 請(qǐng)求完成之后設(shè)置空白頁(yè)的url
fetch(/* ajax */).then(() => {
    tab.location.href = '****'
})

優(yōu)劣

缺點(diǎn)是只能與自己打開的頁(yè)面完成通訊顾孽,應(yīng)用面相對(duì)較窄祝钢;但優(yōu)點(diǎn)是在跨域場(chǎng)景中依然可以使用該方案。

localStorage

具體方案

設(shè)置共享區(qū)域的storage若厚,storage會(huì)觸發(fā)storage事件

// A.html
localStorage.setItem('message', 'hello')

// B.html
window.onstorage = evt => {
  // evt.key, evt.oldValue, evt.newValue
}

tips

  1. 觸發(fā)寫入操作的頁(yè)面下的storage listener不會(huì)被觸發(fā)
  2. storage事件只有在發(fā)生改變的時(shí)候才會(huì)觸發(fā)拦英,即重復(fù)設(shè)置相同值不會(huì)觸發(fā)listener
  3. safari隱身模式下無(wú)法設(shè)置localStorage值

優(yōu)劣

API簡(jiǎn)單直觀,兼容性好测秸,除了跨域場(chǎng)景下需要配合其他方案疤估,無(wú)其他缺點(diǎn)

BroadcastChannel

具體方案

localStorage方案基本一致,額外需要初始化

// A.html
const channel = new BroadcastChannel('tabs')
channel.onmessage = evt => {
    // evt.data
}

// B.html
const channel = new BroadcastChannel('tabs')
channel.postMessage('hello')

優(yōu)劣

localStorage方案沒(méi)特別區(qū)別乞封,都是同域做裙、API簡(jiǎn)單,BroadcastChannel方案兼容性差些(chrome > 58)肃晚,但比localStorage方案生命周期短(不會(huì)持久化)锚贱,相對(duì)干凈些。

SharedWorker

具體方案

SharedWorker本身并不是為了解決通訊需求的关串,它的設(shè)計(jì)初衷應(yīng)該是類似總控拧廊,將一些通用邏輯放在SharedWorker中處理监徘。不過(guò)因?yàn)橐材軐?shí)現(xiàn)通訊,所以一并寫下:

// A.html
var sharedworker = new SharedWorker('worker.js')
sharedworker.port.start()
sharedworker.port.onmessage = evt => {
    // evt.data
}

// B.html
var sharedworker = new SharedWorker('worker.js')
sharedworker.port.start()
sharedworker.port.postMessage('hello')

// worker.js
const ports = []
onconnect = e => {
    const port = e.ports[0]
    ports.push(port)
    port.onmessage = evt => {
        ports.filter(v => v!== port) // 此處為了貼近其他方案的實(shí)現(xiàn)吧碾,剔除自己
        .forEach(p => p.postMessage(evt.data))
    }
}

優(yōu)劣

相較于其他方案沒(méi)有優(yōu)勢(shì)凰盔,此外,API復(fù)雜而且調(diào)試不方便倦春。

Cookie

具體方案

一個(gè)古老的方案户敬,有點(diǎn)localStorage的降級(jí)兼容版,我也是整理本文的時(shí)候才發(fā)現(xiàn)的睁本,思路就是往document.cookie寫入值尿庐,由于cookie的改變沒(méi)有事件通知,所以只能采取輪詢臟檢查來(lái)實(shí)現(xiàn)業(yè)務(wù)邏輯呢堰。

方案比較丑陋抄瑟,勢(shì)必被淘汰的方案,貼一下原版思路地址枉疼,我就不寫demo了皮假。

communication between browser windows (and tabs too) using cookies

優(yōu)劣

相較于其他方案沒(méi)有存在優(yōu)勢(shì)的地方,只能同域使用骂维,而且污染cookie以后還額外增加AJAX的請(qǐng)求頭內(nèi)容惹资。

Server

之前的方案都是前端自行實(shí)現(xiàn),勢(shì)必受到瀏覽器限制席舍,比如無(wú)法做到跨瀏覽器的消息通訊布轿,比如大部分方案都無(wú)法實(shí)現(xiàn)跨域通訊(需要增加額外的postMessage邏輯才能實(shí)現(xiàn))。通過(guò)借助服務(wù)端来颤,還有很多增強(qiáng)方案汰扭,也一并說(shuō)下。

乞丐版

后端無(wú)開發(fā)量福铅,前端定期保存萝毛,在tab被激活時(shí)重新獲取保存的數(shù)據(jù),可以通過(guò)校驗(yàn)hash之類的標(biāo)記位來(lái)提升檢查性能滑黔。

window.onvisibilitychange = () => {
    if (document.visibilityState === 'visible') {
        // AJAX
    }
}

Server-sent Events / Websocket

項(xiàng)目規(guī)模小型的時(shí)候可以采取這類方案笆包,后端自行維護(hù)連接,以及后續(xù)的推送行為略荡。

SSE

// 前端
const es = new EventSource('/notification')

es.onmessage = evt => {
    // evt.data
}
es.addEventListener('close', () => {
    es.close()
}, false)


// 后端庵佣,express為例
const clients = []

app.get('/notification', (req, res) => {
    res.setHeader('Content-Type', 'text/event-stream')
    clients.push(res)
    req.on('aborted', () => {
        // 清理clients
    })
})
app.get('/update', (req, res) => {
    // 廣播客戶端新的數(shù)據(jù)
    clients.forEach(client => {
        client.write('data:hello\n\n')
        setTimeout(() => {
            client.write('event:close\ndata:close\n\n')
        }, 500)
    })
    res.status(200).end()
})

Websocket

socket.iosockjs例子比較多汛兜,略

消息隊(duì)列

項(xiàng)目規(guī)模大型時(shí)巴粪,需要消息隊(duì)列集群長(zhǎng)時(shí)間維護(hù)長(zhǎng)鏈接,在需要的時(shí)候進(jìn)行廣播。

提供該類服務(wù)的云服務(wù)商很多肛根,或者尋找一些開源方案自建辫塌。

例如MQTT協(xié)議方案(阿里云就有提供),web客戶端本質(zhì)上也是websocket派哲,需要集群同時(shí)支持ws和mqtt協(xié)議臼氨,示例如下:

// 前端
// 客戶端使用開源的Paho
// port會(huì)和mqtt協(xié)議通道不同
const client = new Paho.MQTT.Client(host, port, 'clientId')

client.onMessageArrived = message => {
    // message. payloadString
}
client.connect({
    onSuccess: () => {
        client.subscribe('notification')
    }
})
// 抑或,借助flash(雖然快要被淘汰了)進(jìn)行mqtt協(xié)議連接并訂閱相應(yīng)的頻道芭届,flash再通過(guò)回調(diào)拋出消息

// 后端
// 根據(jù)服務(wù)商提供的Api接口調(diào)用頻道廣播接口
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末储矩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子褂乍,更是在濱河造成了極大的恐慌椰苟,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件树叽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡谦絮,警方通過(guò)查閱死者的電腦和手機(jī)题诵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)层皱,“玉大人性锭,你說(shuō)我怎么就攤上這事〗信郑” “怎么了草冈?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)瓮增。 經(jīng)常有香客問(wèn)我怎棱,道長(zhǎng),這世上最難降的妖魔是什么绷跑? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任拳恋,我火速辦了婚禮,結(jié)果婚禮上砸捏,老公的妹妹穿的比我還像新娘谬运。我一直安慰自己,他們只是感情好垦藏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布梆暖。 她就那樣靜靜地躺著,像睡著了一般掂骏。 火紅的嫁衣襯著肌膚如雪轰驳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音滑废,去河邊找鬼蝗肪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蠕趁,可吹牛的內(nèi)容都是我干的薛闪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼俺陋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼豁延!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起腊状,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤诱咏,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后缴挖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體袋狞,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年映屋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苟鸯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡棚点,死狀恐怖早处,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瘫析,我是刑警寧澤砌梆,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站贬循,受9級(jí)特大地震影響咸包,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜甘有,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一诉儒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧亏掀,春花似錦忱反、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至间影,卻和暖如春注竿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工巩割, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留裙顽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓宣谈,卻偏偏與公主長(zhǎng)得像愈犹,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闻丑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容