在 Web 开发中过程中,同一个网址可能要在不同页面打开,页面的操作对其也有相应的影响,在管理系统中尤为常见。比如在一个列表中,我们点击新增打开了一个新的标签页,在此页面新增了一条数据,原来标签页的列表在新增后不能得到及时的反馈,需要用户手动刷新,但是通过js其实是可以实现跨标签页通信的,本文提供几种常见的方法供大家参考。

export default function App() {

const onClick = () => {
    sendMsg("click", { data: "这是一段消息" });
};

useEffect(() => {
    listenMsg((data) => {
    console.log(data);
    });
}, []); 

return (
    <div className="App">
        <h1>跨标签页的数据共享</h1>
        <h2>Start editing to see some magic happen!</h2>
        <button onClick={onClick}>发起通信</button>
    </div>
);}

BroadCast Channel

Broadcast Channel API 可以实现同源下浏览器不同窗口,Tab 页,frame 或者 iframe 下的 浏览器上下文 (通常是同一个网站下不同的页面) 之间的简单通讯。

广播频道会被命名和绑定到指定的源。

通过创建一个监听某个频道下的 BroadcastChannel 对象,你可以接收发送给该频道的所有消息。一个有意思的点是,你不需要再维护需要通信的 iframe 或 worker 的索引。它们可以通过构造 BroadcastChannel 来简单地“订阅”特定频道,并在它们之间进行全双工(双向)通信。

const channel = new BroadcastChannel("echo_channel");  

export const sendMsg = (type, content) => {
      console.log("-------发起通信--------", content);
      channel.postMessage({
          type,
          content
      });
};

export const listenMsg = (callback) => {
      channel.addEventListener("message", (event) => {
        callback && callback(event.data);
      });
};

LocalStorage

只读的localStorage 属性允许你访问一个Document 源(origin)的对象 Storage;存储的数据将保存在浏览器会话中。

export const sendMsg = (type, content) => {
    console.log("-------发起通信--------", content);
    localStorage.setItem(
    "echo-msg",
    JSON.stringify({
        type,
        ...content
        })
    );
};  

export const listenMsg = (callback) => {
    window.addEventListener("storage", (event) => {
        console.log(event); 
        const { newValue } = event;
        try {
            const msg = JSON.parse(newValue);
            callback && callback(msg);
        } catch (err) {
        
        // 处理错误
        
        }
    }
    });
};

Window.postMessage

window.postMessage()  方法可以安全地实现跨源通信。通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为 https),端口号(443 为 https 的默认值),以及主机 (两个页面的模数 Document.domain设置为相同的值) 时,这两个脚本才能相互通信。

export const sendMsg = (type, content) => {
    console.log("-------发起通信--------", content);
    // let receiver = document.getElementById('receiver').contentWindow;
    // window.parent.postMessage(JSON.stringify({
    // from: 'auth',
    // event: 'close',
    // code: 1
    // }), '*')
    let receiver = window.open("https://lhp8dw.csb.app/");
    receiver.postMessage({ type, content });
};

export const listenMsg = (callback) => {
    window.addEventListener("message", (event) => {
    callback && callback(event);
});
};

Service Worker

Service worker 是一个注册在指定源和路径下的事件驱动 worker。它采用 JavaScript 文件的形式,控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。你可以完全控制应用在特定情形(最常见的情形是网络不可用)下的表现。

Service worker 运行在 worker 上下文:因此它无法访问 DOM,相对于驱动应用的主 JavaScript 线程,它运行在其他线程中,所以不会造成阻塞。它被设计为完全异步;因此,同步 XHR 和 Web Storage 不能在 service worker 中使用。

SharedWorker 定时轮询

SharedWorker 接口代表一种特定类型的 worker,可以从几个浏览上下文中访问,例如几个窗口、iframe 或其他 worker。它们实现一个不同于普通 worker 的接口,具有不同的全局作用域。

IndexedDB 定时轮询

IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。

const request = indexedDB.open("database", 1);

export const sendMsg = (type, content) => {
  console.log("-------发起通信--------", content);
  request.onupgradeneeded = (event) => {
    const db = event.target.result;
    const objectStore = db.createObjectStore("dataStore", {
      keyPath: "key",
    });
  };

  request.onsuccess = (event) => {
    const db = event.target.result;
    const transaction = db.transaction(["dataStore"], "readwrite");
    const objectStore = transaction.objectStore("dataStore");

    // 存储数据
    objectStore.put({ key: "supper", value: `moment` });
    transaction.oncomplete = () => {
      db.close();
    };
  };


export const listenMsg = (callback) => {
     setInterval(() => { 
        request.onsuccess = (event) => {
         const db = event.target.result;
         const transaction = db.transaction(["dataStore"], "readonly");
         const objectStore = transaction.objectStore("dataStore");

          // 获取数据
          const getRequest = objectStore.get("supper");
          getRequest.onsuccess = (event) => {
            const data = event.target.result;
            if (data && callback) {
              callback(data.value);
            }
        };
        transaction.oncomplete = () => {
          db.close();
        };
      };
     1000
    );
});
};

Cookie 定时轮询

export const sendMsg = (type, content) => {
    console.log("-------发起通信--------", content);
    document.cookie = `${type}=${content}`;
};

export const listenMsg = (callback) => {
     setInterval(() => { 
         callback && callback(document.cookie); }, 
     1000
     );
});
};

Websocket

WebSockets 是一种先进的技术。它可以在用户的浏览器和服务器之间打开交互式通信会话。使用此 API,你可以向服务器发送消息并接收事件驱动的响应,而无需通过轮询服务器的方式以获得响应。

// 创建一个 WebSocket 连接 
const socket = new WebSocket('ws://example.com');

export const sendMsg = (type, content) => {
    console.log("-------发起通信--------", content);
    socket.onopen = () => { 
        socket.send(content); 
    };
};

export const listenMsg = (callback) => {
    socket.onmessage = function(event) { 
        callback && callback(event.data)
    };
   });
};