发布订阅模式

发布订阅模式

一月 07, 2021

发布订阅模式

参考资料

  1. https://www.jianshu.com/p/c1be274d94cb

写在前面

订阅发布模式可以用在前端不同组件通讯的问题

正篇

简单来说就是通过在不同组件中引入一个公共类,该类中封装了一个存放key值,和方法队列的数组,以及一个订阅、触发、取消订阅的方法。使用时通过调用订阅方法并传入一个能够修改需要传递值的方法,订阅方法会将方法存放到对应key值的方法队列中。在需要传递值时通过调用触发方法,触发方法会依次调用对应key值的方法队列中的方法,从而对值进行修改。取消订阅的方法则是将对应的方法从方法队列中移除,下面是使用react hook的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 订阅发布类
export default {
// 方法队列
listenList: {},
// 添加订阅 传入key与回调方法
listen: function (key, fun) {
// 首次为空
if (!this.listenList[key]) {
this.listenList[key] = [];
}
// 加入队列
this.listenList[key].push(fun);
},
// 触发方法
tigger: function (key, value) {
let funs = this.listenList[key];
// 队列为空的情况
if (!funs || funs.length === 0) {
return false;
} else {
// 依次触发
funs.forEach((fun) => {
fun.apply(this, [value]);
});
}
},
// 取消订阅
remove: function(key, fun){
let funs = this.listenList[key];
// 没有key值
if(!funs){
return false;
}
// 没有传入方法的情况则视为清空所有订阅
if(!fun){
fns && (fns.length = 0);
}else{
// 对比并移除
for(let i = fns.length - 1; i >=0; i--) {
if(fns[i] === fun) {
fns.splice(i, 1)
}
}
}
},
};

组件一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import React, { useState, useEffect } from 'react';
import SubPub from '@/utils/SubPub.js';

export default function App() {
const [price1, setPrice1] = useState(-1);
const [price2, setPrice2] = useState(-1);

useEffect(() => {
// 添加订阅
let changePrice1 = (value) => {
console.log('comp1:', value);
setPrice1(value);
}
SubPub.listen('price1', changePrice1);

let changePrice2 = (value) => {
console.log('comp1:', value);
setPrice2(value);
}
SubPub.listen('price2', changePrice2);

// 销毁时取消订阅
return ()=>{
SubPub.remove('price1', changePrice1);
SubPub.remove('price2', changePrice2);
}
}, []);// 只触发一次
return (
<>
<h2>This is comp1</h2>
<h2>{price1}</h2>
<h2>{price2}</h2>
<button onClick={() => SubPub.tigger('price1', 1)}>prices1 to 1</button>
</>
);
}

组件二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import React, { useState, useEffect } from 'react';
import SubPub from '@/utils/SubPub.js';

export default function App() {
const [price1, setPrice1] = useState(-1);
const [price2, setPrice2] = useState(-1);

useEffect(() => {
// 添加订阅
let changePrice1 = (value) => {
console.log('comp2:', value);
setPrice1(value);
}
SubPub.listen('price1', changePrice1);

let changePrice2 = (value) => {
console.log('comp2:', value);
setPrice2(value);
}
SubPub.listen('price2', changePrice2);

// 销毁时取消订阅
return ()=>{
SubPub.remove('price1', changePrice1);
SubPub.remove('price2', changePrice2);
}
}, []); // 只触发一次
return (
<>
<h2>This is comp2</h2>
<h2>{price1}</h2>
<h2>{price2}</h2>
<button onClick={() => SubPub.tigger('price1', 2)}>prices1 to 2</button>
</>
);
}

应用这两个组件的类

1
2
3
4
5
6
7
8
9
10
11
12
import React, { useState, useEffect } from 'react';
import P1 from '@/pages/page1/index.js';
import P2 from '@/pages/page2/index.js';

export default function App() {
return (
<>
<P1></P1>
<P2></P2>
</>
);
}

最终效果

http://r.photo.store.qq.com/psc?/V51omi8H2ZybVP1W54I94cQfjz1u0MYq/45NBuzDIW489QBoVep5mca8NoZqAQv7K1Wum*FnKJP2EkNuiggqCTtrz2djnEa8DrTRiUfVjqnBnnM.58MWciLu6l0MOIrl1QToYRYNXx28!/r