做爲一個大四的老油條,對於即將畢業的小編來說,畢業設計是逃不掉的,最近在着手開始寫畢設的後臺管理系統,這個後臺管理系統的前端用的 Vue
+ iframe
實現tab標籤頁,但經過這種方式實現管理系統頁面會遇到一個問題,例如:經過商品列表頁打開一個修改商品信息tab頁,修改完後想要讓商品列表頁自動刷新對應的商品信息。javascript
對於這個問題可能會想到,子頁面調用父頁面的方法而後經過父頁面調用對應的子頁面來進行更新內容,但這種方式會增長頁面之間的耦合度並且也很蠢,咱們不妨把這個機制理解爲 跨頁面通訊 ,而前端跨頁面通訊的方式有不少種,這裏小編採用「發佈 — 訂閱」的設計模式以及跨頁面通訊的兩種最簡單的方式來實現。html
更多的跨頁面通訊的方式可查看這片文章:跨頁面通訊的各類姿式前端
發佈訂閱模式是指訂閱者(Subscriber)經過一個主題(Theme)和自定義事件(Event)進行消息訂閱,當發佈者(Publisher)經過發佈主題的方式通知各個訂閱該主題的Subscriber執行綁定的回調事件。vue
優勢:java
基於發佈訂閱模式常見的案例有:windows
window.addEventListener("click", event)
。其中,在Vue裏組件之間經過eventBus進行通訊是典型的「發佈 - 訂閱」模式的例子,用法以下:設計模式
首先建立 bus.js
,建立新的Vue實例並導出。api
import Vue from 'vux'
export default new Vue()
複製代碼
接着在組件A和組件B中引入bus.js:import Bus from '@/utils/bus
,組件A在 mounted
鉤子中調用 Bus
的註冊訂閱方法 $on
傳入訂閱主題和回調方法,組件B中在點擊事件中發佈主題,讓訂閱該主題的組件執行回調方法。瀏覽器
// 組件A
mounted () {
Bus.$on('SayHollow', text => {
console.log(text)
})
}
複製代碼
// 組件B
methods: {
clickEvent () {
Bus.$emit('SayHollow', '啊俊俊')
}
}
複製代碼
那麼接下來小編將參照 Vue 的 eventBus 來實現基於「發佈 - 訂閱」模式的跨頁面通訊,注意這裏的跨頁面通訊並非Vue中的跨頁面通訊,而是跨瀏覽器tab頁面通訊。緩存
localStorage是前端經常使用的本地存儲,它用於長久保存整個網站的數據,保存的數據沒有過時時間,直到手動去刪除,但localStorage有一個StorageEvent事件可能不太瞭解。
在同源頁面下,localStorage的內容發生改變後,會觸發 storage
事件並執行相應的回調函數,因此咱們能夠根據這個特性實現同源頁面下跨頁面通訊。
同源頁面:遵循同源策略,即兩個頁面的協議、域名、端口、主機要徹底相同,則這兩個頁面爲同源頁面。
// http://localhost:8080/A.html
window.addEventListener('storage', e => {
// e.key 改變的key值 - msg
// e.oldValue 改變前的值
// e.newValue 改變後的值 - 哈哈哈
if (e.key === 'msg') { ... }
})
// http://localhost:8080/B.html
localStorage.setItem('msg', '哈哈哈')
複製代碼
觸發listener條件
localStorage.setItem
操做的頁面沒法觸發listener事件。// CrossPageMsg.js
class Listener {
constructor (theme, fn) {
this.theme = theme
this.fn = fn
this.open_status = true
this.handle = this.handle.bind(this)
}
handle (e) {
// 若是改變的storage的key不是CrossPageMsg就忽略
if (e.key !== 'CrossPageMsg') return
let info = JSON.parse(e.newValue)
if (info.theme === this.theme && this.open_status) {
this.fn(...info.args)
}
}
change (fn) {
this.fn = fn
}
open () {
this.open_status = true
}
close () {
this.open_status = false
}
off () {
window.removeEventListener('storage', this.handle)
}
}
export default {
// 訂閱函數
'$on': (theme, fn) => {
const listener = new Listener(theme, fn)
window.addEventListener('storage', listener.handle)
return listener
},
// 發佈函數
'$emit': (theme, ...args) => {
if (typeof theme !== 'string') return
localStorage.setItem('CrossPageMsg', JSON.stringify({
theme,
args,
random: Math.random() * 10
}))
},
// 關閉訂閱
'$off': (listener) => {
if (listener instanceof Listener) {
listener.off()
}
}
}
複製代碼
以上是核心代碼的實現,使用方式很簡單,在 main.js
引入該文件並設置 Vue.prototype.$Cross = Cross
便可在組件中使用,如下是使用方式。
// main.js
import Vue from 'vue'
import Cross from 'CrossPageMsg.js'
Vue.prototype.$Cross = Cross
...
複製代碼
// PageA.vue - 訂閱者A(Subscriber)
<template>
<div>
<div>我是訂閱者A, 訂閱的主題是GetStudent</div>
<button @click="CreateListener">建立訂閱</button>
<button @click="OffListener">移除訂閱</button>
<button @click="listener.close()">關閉訂閱</button>
<button @click="listener.open()">開啓訂閱</button>
<button @click="ChangeEvent">開啓訂閱</button>
<div v-for="(item, index) in stu_list" :key="index">{{item.name}}, {{item.age}}</div>
</div>
</template>
<script>
export default {
data () {
return {
listener: '',
stu_list: []
}
},
methods: {
// 註冊訂閱
CreateListener () {
this.listener = this.$Cross.$on('GetStudent', (name, age) => {
console.log('我是訂閱者A,我訂閱的主題是GetStudent')
console.log('我收到的信息是', name, age)
this.stu_list.push({ name, age })
})
},
// 移除訂閱
OffListener () {
// 調用listener自身off方法移除訂閱
this.listener.off()
// 調用$Cross.$off,傳入listener移除訂閱
// this.$Cross.$off(this.listener)
},
// 修改listener回調
ChangeEvent () {
this.listener.change((name, age) => {
console.log(`你好 ${name}`)
})
}
},
mounted () {
this.CreateListener()
}
}
</script>
複製代碼
// Send.vue - 發佈者(Publisher)
<template>
<div>
<div>發送 [GetStudent] 主題,信息:小明,16歲</div>
<button @click="Send1">發送</button>
<div>發送 [GetStudent] 主題,信息:小張,18歲</div>
<button @click="Send2">發送</button>
<div>發送 [GetGrade] 主題,信息:一年級</div>
<button @click="Send3">發送</button>
</div>
</template>
<script>
export default {
methods: {
Send1 () {
this.$Cross.$emit('GetStudent', '小明', '16歲')
},
Send2 () {
this.$Cross.$emit('GetStudent', '小張', '18歲')
},
Send3 () {
this.$Cross.$emit('GetGrade', '一年級')
}
}
}
</script>
複製代碼
50行代碼都不到就完成了,CrossPageMsg.js
中的代碼是整個功能的核心代碼。
$on
傳入 theme
和回調方法註冊 listener
,註冊後的 listener
還有其餘的api(下文會介紹)。$emit
用來發布主題消息,傳入的第一個參數是 theme
,其餘參數則依次傳入回調方法中。$off
負責移除 listener
,要是使用過Vue中的eventBus的小夥伴應該不陌生。可能不少小夥伴都不知道 Broadcast Channel
是什麼?在 MDN 上面的解釋是這樣子的:
The BroadcastChannel interface represents a named channel that any browsing context of a given origin can subscribe to. It allows communication between different documents (in different windows, tabs, frames or iframes) of the same origin. Messages are broadcasted via a message event fired at all BroadcastChannel objects listening to the channel.
意思就是「廣播頻道」,它能夠在同源頁面下建立一個廣播消息頻道,當不一樣頁面監聽該頻道後,某個頁面向該頻道發出消息後會被監聽該頻道的頁面所接收並進行回調。
// A頁面監聽廣播
// 第一步 建立實例
const bc = new BroadcastChannel('myBroadcastChannel')
// 第二部 經過onmessage設置回調事件
bc.onmessage = e => {
console.log(e.data)
}
// B頁面發送廣播
const bc = new BroadcastChannel('myBroadcastChannel')
bc.postMessage('hollow word')
// 關閉廣播
bc.close()
複製代碼
觸發onmessage條件
new BroadcastChannel
建立實例時傳入參數一致。postMessage
操做的實例沒法觸發自身的 onmessage
事件。兼容性問題
雖說Broadcast Channel的api很是簡單,在跨頁面通訊上有着出色的表現,但對於萬惡的 IE瀏覽器 來說,兼容性就不那麼樂觀了,這裏能夠看到在 Can I Use 上的兼容性,目前最新版本的IE都不兼容。
雖然兼容性不太友好,但也實現如下吧~~
// Broadcast.js
class Listener {
constructor (theme, fn) {
this.theme = theme
this.open_status = true
this.bc = new BroadcastChannel(theme)
this.change(fn)
}
change (fn) {
this.bc.onmessage = e => {
if (this.open_status) {
fn(...e.data.args)
}
}
}
open () {
this.open_status = true
}
close () {
this.open_status = false
}
off () {
this.bc.close()
}
}
export default {
// 訂閱者
'$on': (theme, fn) => {
return new Listener(theme, fn)
},
// 發佈者
'$emit': (theme, ...args) => {
const bc = new BroadcastChannel(theme)
bc.postMessage({ theme, args })
bc.close()
},
// 關閉訂閱
'$off': (listener) => {
if (listener instanceof Listener) {
listener.close()
}
}
}
複製代碼
以上是核心代碼,使用方式和localStorage方式的一毛同樣,這裏就不把代碼寫出來了。
簡單的整理下API,第一次寫,寫得很差莫怪~~
方法名 | 說明 | 傳參 | 返回 |
---|---|---|---|
$on | 註冊跨頁面訂閱事件 | theme ,callback |
Listener |
$emit | 傳入 theme 和 params 發送跨頁面消息 |
theme , ...params |
- |
$off | 註銷 Listener |
Listener |
- |
方法名 | 說明 | 傳參 |
---|---|---|
change | 修改listener的回調事件 | Function |
open | 開啓listener回調事件 | - |
close | 關閉listener回調事件,關閉後可調用 listener.open() 從新開啓 |
- |
off | 註銷listener,和 close 的區別是註銷後調用 open 也沒法執行回調 |
- |
Vue
的 eventBus
進行跨組件通訊。localStorage
的 storage
事件進行跨頁面通訊。BroadcastChannel
廣播消息頻道進行跨頁面通訊(若是要兼容萬惡根源IE瀏覽器的話不建議使用)。Vue
的 eventBus
和兩種跨頁面通訊方式實現基於「發佈 — 訂閱」模式的跨頁面通訊。