1

问题

两个窗口,显示组件,A窗口显示A组件,B窗口显示B组件,两个组件共同订阅一个Service中的Subject,当在A组件中进行修改后,Service中的Subject发送通知,“值变更了”,B组件订阅不到。

image.png

现在打开两个窗口,每个窗口显示一个组件,每个组件都订阅 Service 中的 subjectTest, 在AComponent组件中,每隔一秒发送一条数据:

AComponent 组件中

    let i = 0;
    interval(1000).subscribe(() => {
      this.systemService.abc(++i);
    })

    this.systemService.subjectTest.subscribe(v => {
      console.log("AComponent收到了通知", v);
    });

Service:

subjectTest = new Subject<any>();

abc(s :any) {
 console.log('发送通知', s)
 this.subjectTest.next(s)
}

BComponent组件中也订阅:

this.systemService.subjectTest.subscribe(v => {
      console.log('B Component 组件收到了通知',v);
    })

效果如下:
蓝色窗口:AComponet

红色窗口:BComponent

可以看AComponent一值在调用abc()方法,发送通知,AComponent组件收到了通知,BComponent组件并没有。

image.png

问题分析

我们当前 编写的都是 ts 代码,ts不能在 浏览器中直接运行,需要编译 成js文件,在浏览器中运行。

JS 内存模型

js的内存主要分为两个部分:栈 和 堆

栈 (Stack)

后进先出原则

🌰: 当调用一个函数时,函数的局部变量、参数和返回地址会被压入栈,函数调用结束后,数据会从栈中弹出。

堆 (Heap)

存储复杂和动态分配的对象

🌰: 声明一个全局变量,引入service、定义Subject,都存储在堆中。

浏览器新建一个窗口

每个窗口都是独立的 栈 和 堆 :
在:蓝色窗口中定义一个a变量,进行打印,没问题,在红色窗口中输出输出 a 是一个 为定义的变量。
image.png

所以当在AComponet 中订阅了subjectTest,调用了 abc(), 发送了推送,AComponet组件也接受到了,而BComponent 虽然订阅了subject 却收不到通知,是因为两个 sujectTest 是两个独立的可订阅变量。

解决

利用浏览器的 localStorage 事件

在AComponet 组件中加入:localStorage事件

因为需要在AComponet 组件中发送 通知,那么当AComponet 收到通知后,发送localStorage 事件

image.png

在BComponent 组件中监听storage 事件,在判断是否是响应 键。

window.addEventListener('storage', (event) => {
      if (event.key === 'push') {
        console.log('ndexComponent 组件收到了通知', event.newValue);
      }
    });

只有 同源 页面(相同的协议、域名和端口)才会响应 storage 事件。
在同一个标签页中对 localStorage 的修改不会触发 storage 事件。

蓝色窗口:AComponet

红色窗口:BComponent

image.png

使用 Broadcast Channel

一个所有同源页面都可以共享的(广播)频道,因此其中某一个页面发送的消息可以被其他页面监听到。

在A组件中继续每隔 3s 发布一次

    let i = 0;
    interval(1000 * 3).subscribe(() => {
      this.systemService.abc(++i);
    });

service 中定义 BroadcastChannel,创建通道名称为“push”,只有频道相同才互通。

channel = new BroadcastChannel('push');

abc(s :any) {
    console.log('发送通知', s)
    this.channel.postMessage(s);
  }

B组件:

this.systemService.channel.onmessage = (event) => {
      console.log('BComponent 组件收到了通知',event.data);
    }

效果:

蓝色窗口:AComponet

红色窗口:BComponent

image.png

总结:

两者都可以跨窗口同步信息, 两者最主要的区别的在于,数据持久化。
localStorage的数据保存在浏览器中,刷新页面,数据依旧存在。
BroadcastChannel的数据在会话中。刷新页面,之前的数据就没有了,得重新开始。

相对使用环境:使用BroadcastChannel更适合我们,刷新后,我们重新请求后台,已是最新数据,而且更改轮询时间等,都是看着不对眼,才会设置。


zZ_jie
431 声望9 粉丝

虚心接受问题,砥砺前行。