做为一个大四的老油条,对于即将毕业的小编来说,毕业设计是逃不掉的,最近在着手开始写毕设的后台管理系统,这个后台管理系统的前端用的 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
和两种跨页面通讯方式实现基于“发布 — 订阅”模式的跨页面通讯。