likes
comments
collection
share

🚀纯前端怎么实现不同标签页下的数据通信🚀

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

前言

我记得之前有做过这样的一个需求——可以简化的看作为一个 TODO List ,我打开了两个 tab 页,都是对应这个 TODO List

然后我在 A 页面新增一个,不出意外的话,在 B 页面是看不到的。产品当时跟我提了一下这个,让测试给我提了个 Bug

我那个时候下意识的反应就是找后端接一下 WebSocket ,但接一个 WebSocket 谈何简单,单单是公司网关那里分分钟让你搞一天都搞不定。

我就接着搜了一下,看看有没有纯前端的实现方案,还真让我找到了。今天我们就一起讨论下:纯前端如何实现不同标签页下的数据通信。

TODO List Demo

这里我为了演示,做了一个最简版本的 TODO List ,大家知道意思就行:

import { Button, Input, Row } from "antd";
import { useState } from "react";

const Todo = () => {
  const [list, setList] = useState([]);
  const [value, setValue] = useState("");
  const handleCreate = () => {
    if (!value) {
      return;
    }
    const arr = [...list];
    arr.push(value);
    setList(arr);
    setValue("");
  };
  return (
    <div style={{ margin: 20 }}>
      <Row>
        <Input
          value={value}
          onInput={(e) => setValue(e.target.value)}
          style={{ width: 200, marginRight: 16 }}
          placeholder="输入点什么"
        />
        <Button onClick={handleCreate} type="primary">
          增加
        </Button>
      </Row>
      <ul>
        {list.map((item) => {
          return <li>{item}</li>;
        })}
      </ul>
    </div>
  );
};

export default Todo;

🚀纯前端怎么实现不同标签页下的数据通信🚀

代码很简单,仅为了后续演示数据同步使用。就是用一个输入框加一个确认按钮,然后把输入的东西 push 到数组里面,把这个数组渲染出来。

localStorage

localStorage 相信各位前端同学都不会陌生,数据存储在 localStorage 中是持久的,即使用户关闭了浏览器窗口,数据也会保留下来,直到用户手动清除或者被JS脚本清除。

在同源的情况下,localStorage存储的数据是共享的,也就是说假设我打开了两个同源的标签页 AB ,我在 A 中写入了 localStorage ,在 B 标签页是可以获取到的。

且我们是可以监听 localStorage 的变化的,既然数据能共享,取到的是同一份。而且还能监听数据的变化,这不就可以实现数据通信同步了吗?

首先,在 list 变化的时候写入 localStorage

useEffect(() => {
    window.localStorage.setItem(CACHE_KEY, JSON.stringify(list));
}, [list]);

然后在 localStorage 变更的时候更新 state 。注意:localStorage 的变化事件 (storage 事件) 在其他标签页或窗口修改 localStorage 时才会触发。

  const handleStorageChange = useCallback(
    (event) => {
      const { key, newValue } = event;
      if (isEmpty(newValue)) {
        setList([]);
      }
      if (key === CACHE_KEY) {
        if (isEmpty(newValue)) {
          setList([]);
        } else if (JSON.stringify(list) !== newValue) {
          setList(JSON.parse(newValue));
        }
      }
    },
    [list]
  );

  useEffect(() => {
    window.addEventListener("storage", handleStorageChange);
    return () => {
      window.removeEventListener("storage", handleStorageChange);
    };
  }, [handleStorageChange]);

所以为了验证这个功能,我们可以打开两个标签页来看看效果:

🚀纯前端怎么实现不同标签页下的数据通信🚀

可以看到,在 A 标签页输入的东西可以顺利同步到 B 标签页中,也是实现了纯前端下的数据通信。

但可能有人觉得,本身我这个功能不需要依赖 localStorage ,搞得我现在又需要在本地存储再存一份。有没有不依赖本地存储的跨标签页数据通信方式呢?

Broadcast Channel API

答案是有的,那就是——Broadcast Channel API,我们可以一起来看看 MDN 对它的解释:

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

也就是说这个 API 设计出来就是为了让我们对同源的不同窗口做数据通信的。

看一下兼容性:

🚀纯前端怎么实现不同标签页下的数据通信🚀

除了 IE 不支持,其他大多数主流浏览器都支持。

整体的 API 用起来十分简单,我们一起来看看。

首先刚开始需要加入或者创建一个频道,跟我们使用 ws 的时候一样,需要创建或者加入一个房间,组件销毁时退出频道。

  useEffect(() => {
    const bc = new BroadcastChannel(CHANNEL_NAME);
    channel.current = bc;
    channel.current.onmessage = function (ev) {
      if (ev?.data?.type === "add") {
        const arr = [...list];
        arr.push(ev.data.value);
        setList(arr);
      }
    };
    return () => {
      channel.current && channel.current.close();
    };
  }, [list]);

onmessage 中就可以接收到频道中其他标签页发送过来的消息,在这里获取到消息之后更新 state 。同样的,在新增数据的时候,通过频道发送消息。

  const handleCreate = () => {
    if (!value) {
      return;
    }
    const arr = [...list];
    arr.push(value);
    setList(arr);
    setValue("");
    channel.current.postMessage({
      type: "add",
      value,
    });
  };

一样也是可以实现跨标签页的数据通信的。

最后

以上就是本文的全部内容,主要介绍了两种纯前端实现不同标签页下的数据通信的方式,如果你觉得有意思的话,点点关注点点赞吧~

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