likes
comments
collection
share

通过localStorage同源发送信息

作者站长头像
站长
· 阅读数 35

背景 页面A是企业列表页,页面B是企业详情面。页面B上面更新了企业信息,需要通知给页面A,同时更新列表中的企业信息

postMessage 在做这个需求的时候,最先考虑到的就是 postMessage 。这个方法可以安全的实现跨源通信,从广义上讲,一个窗口可以获得对另一个窗口的引用(比如 targetWindow=window.opener),然后再窗口上调用 targetWindow.postMessage() 来发送一个Message消息。

实现 第一步是获取 window 对象

const targetWindowName = "windowName";  
const newWindow: Window = window.open('', targetWindowName);

这里就用到了一个比较偏的东西。那就是window的name特性。

如果命名为'windowName'的这个窗口已经打开,那么调用window.open就会聚焦到该窗口 如果还没有打开或者已经关闭了,就会重新打开该窗口 基于这个特性,就可以来判断窗口是否已经被打开,并且通过该判端可以得到通过window.open拿到的window对象是否使我们想要的。

// 窗口还未打开或者已经关闭  
if (newWindow?.location?.href === 'about:blank') {
  // 把当前窗口激活  
  window.focus();
  // 关闭窗口,并且不需要操作  
  newWindow.close();
} else {
  // 把当前窗口激活  
  window.focus();
  // 发送postMessage信息  
  newWindow.postMessage(value, window.location.host);
}

问题 在这样子使用postMessage的时候窗口会闪白一下。

那么为什么不把新打开窗口的window给放到内存里呢。这里主要存在一种操作,如果已经打开了企业列表页面,然后再打开一个企业列表页面,并且同时打开同一个企业详情页面,这样子其中一个企业详情改变了之后,只能影响到一个企业列表页面

所以在取舍之后,把这套方案给放弃了。

localStorage localStorage可以允许访问同源的本地存储对象Storage。 这句话给了我很大的启发,同源下是不是只要通过监听storage就可以完成跨页面的数据的传递

结果还真的找到了这个这个监听事件

window.addEventListener('storage', (e) => {});

这样子就可以监听到了localStorage的变化

那么该怎么触发呢?

// 定义好要设置的数据的名称,并且赋值

/**  
 * data = {  
 * source: string, // 标明事件的名称  
 * data: any, // 事件需要传递的数据  
 * }  
 */  
localStorage.setItem('postMessage', data);  
// 清除数据  
localStorage.removeItem('postMessage');

这样子就会触发上面的addEventListener事件。

只需要对该事件进行一次判断就可以完成localStorage的监听了

window.addEventListener('storage', (e) => {
// 判断localStorage改动的key为'postMessage' 并且是有值(确保不是删除操作)  
  if (e.key === 'postMessage' && e.newValue) {
    let value: any = e.newValue;
    try {
      value = JSON.parse(value);
    } catch (error) {
    console.error(error);
    }
  console.log(value);
  }
});

代码封装 可以通过value对事件进行操作分发

订阅-发布设计模式(Event Bus)

  // listener.ts  
  export default class <T = string> {
    // 事件  
    handlers: Map<T, Function[]> = new Map();

    /* 监听事件 */
    on = (eventType: T, handler: Function) => {
      const handlers = this.handlers.get(eventType);
      if (!handlers) {
        this.handlers.set(eventType, [handler]);
        return;
      }
      handlers.push(handler);
    };

    /* 移除事件 */
    off = (eventType: T, handler: Function) => {
      const handlers = this.handlers.get(eventType);
      const idx = handlers?.indexOf(handler);
      if (idx && handlers) handlers.splice(idx, 1);
    };

    /** 响应事件 */
    emit = (eventType: T, data?: any) => {
      const handlers = this.handlers.get(eventType);
      if (!handlers) return;
      handlers.forEach((handler) => handler(data));
    };
  }

事件注册、分发 // event.ts

  import Listener from './listener';
  const listener = new Listener();

  window.addEventListener('storage', (e) => {
    if (e.key === 'postMessage' && e.newValue) {
      let value: any = e.newValue;
      try {
        value = JSON.parse(value);
      } catch (error) {
        console.error(error);
      }
      // 发布事件  
      listener.emit(value.source, value.data);
    }
  });

// 订阅事件

export function eventOn(eventName: string, callback: any) {  
  listener.on(eventName, callback);  
}

// 发布事件

export function eventEmit(eventName: string, value: any) {
  let data: any = {
    source: eventName,
    data: value,
  };
  try {
    data = JSON.stringify(data);
  } catch (e) {
    console.error(e);
  }
  localStorage.setItem('postMessage', data);
  localStorage.removeItem('postMessage');
}

最后 这里想要提一下sessionStorage和localStorage在做通信时候踩的一个坑

localStorage是同源共享的Storage对象,所以在同源下,一个页面修改了localStorage其他页面可以监听到 sessionStorage就不是共享Storage对象了,打开页面后是复制一个Storage对象。什么意思呢,A、B两个同源页面,给A页面加了个一个名为test的sessionStorage的值,在B页面观察,发现并没有添加上去,只有A页面才有。如果这时候关掉B页面,再打开B页面,这时候B页面也有了这个名为test的sessionStorage的值.

转载自:https://juejin.cn/post/7223926748287516729
评论
请登录