做者簡介:nekron 螞蟻金服·數據體驗技術團隊javascript
將跨頁面通信類比計算機進程間的通信,其實方法無外乎那麼幾種,而web領域能夠實現的技術方案主要是相似於如下兩種原理:html
因爲第二種原理更利於解耦業務邏輯,具體的實現方案比較多樣。如下是具體的實現方案,簡單介紹下,權當科普:前端
父頁面經過window.open(url, name)
方式打開的子頁面能夠獲取句柄,而後經過postMessage完成通信需求。java
// parent.html
const childPage = window.open('child.html', 'child')
childPage.onload = () => {
childPage.postMessage('hello', location.origin)
}
// child.html
window.onmessage = evt => {
// evt.data
}
複製代碼
window.open
的第二個name參數時,再次調用window.open('****', 'child')
會使以前已經打開的同name子頁面刷新window.open
會被瀏覽器阻止,不過能夠經過句柄設置子頁面的url便可實現相似效果// 首先先開一個空白頁
const tab = window.open('about:blank')
// 請求完成以後設置空白頁的url
fetch(/* ajax */).then(() => {
tab.location.href = '****'
})
複製代碼
缺點是隻能與本身打開的頁面完成通信,應用面相對較窄;但優勢是在跨域場景中依然可使用該方案。git
設置共享區域的storage,storage會觸發storage事件github
// A.html
localStorage.setItem('message', 'hello')
// B.html
window.onstorage = evt => {
// evt.key, evt.oldValue, evt.newValue
}
複製代碼
API簡單直觀,兼容性好,除了跨域場景下須要配合其餘方案,無其餘缺點web
和localStorage
方案基本一致,額外須要初始化ajax
// A.html
const channel = new BroadcastChannel('tabs')
channel.onmessage = evt => {
// evt.data
}
// B.html
const channel = new BroadcastChannel('tabs')
channel.postMessage('hello')
複製代碼
和localStorage
方案沒特別區別,都是同域、API簡單,BroadcastChannel
方案兼容性差些(chrome > 58),但比localStorage
方案生命週期短(不會持久化),相對乾淨些。chrome
SharedWorker
自己並非爲了解決通信需求的,它的設計初衷應該是相似總控,將一些通用邏輯放在SharedWorker中處理。不過由於也能實現通信,因此一併寫下:express
// 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) // 此處爲了貼近其餘方案的實現,剔除本身
.forEach(p => p.postMessage(evt.data))
}
}
複製代碼
相較於其餘方案沒有優點,此外,API複雜並且調試不方便。
一個古老的方案,有點localStorage
的降級兼容版,我也是整理本文的時候才發現的,思路就是往document.cookie
寫入值,因爲cookie的改變沒有事件通知,因此只能採起輪詢髒檢查來實現業務邏輯。
方案比較醜陋,勢必被淘汰的方案,貼一下原版思路地址,我就不寫demo了。
communication between browser windows (and tabs too) using cookies
相較於其餘方案沒有存在優點的地方,只能同域使用,並且污染cookie之後還額外增長AJAX的請求頭內容。
以前的方案都是前端自行實現,勢必受到瀏覽器限制,好比沒法作到跨瀏覽器的消息通信,好比大部分方案都沒法實現跨域通信(須要增長額外的postMessage邏輯才能實現)。經過藉助服務端,還有不少加強方案,也一併說下。
後端無開發量,前端按期保存,在tab被激活時從新獲取保存的數據,能夠經過校驗hash之類的標記位來提高檢查性能。
window.onvisibilitychange = () => {
if (document.visibilityState === 'visible') {
// AJAX
}
}
複製代碼
項目規模小型的時候能夠採起這類方案,後端自行維護鏈接,以及後續的推送行爲。
// 前端
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) => {
// 廣播客戶端新的數據
clients.forEach(client => {
client.write('data:hello\n\n')
setTimeout(() => {
client.write('event:close\ndata:close\n\n')
}, 500)
})
res.status(200).end()
})
複製代碼
socket.io
、sockjs
例子比較多,略
項目規模大型時,須要消息隊列集羣長時間維護長連接,在須要的時候進行廣播。
提供該類服務的雲服務商不少,或者尋找一些開源方案自建。
例如MQTT協議方案(阿里雲就有提供),web客戶端本質上也是websocket,須要集羣同時支持ws和mqtt協議,示例以下:
// 前端
// 客戶端使用開源的Paho
// port會和mqtt協議通道不一樣
const client = new Paho.MQTT.Client(host, port, 'clientId')
client.onMessageArrived = message => {
// message. payloadString
}
client.connect({
onSuccess: () => {
client.subscribe('notification')
}
})
// 抑或,藉助flash(雖然快要被淘汰了)進行mqtt協議鏈接並訂閱相應的頻道,flash再經過回調拋出消息
// 後端
// 根據服務商提供的Api接口調用頻道廣播接口
複製代碼
原文地址: github.com/ProtoTeam/b…