数据可视化大屏项目,我是如何实现多个大屏之间数据通信的

3,760 阅读7分钟

前言

提到数据可视化,那么相信大家第一反应想到的都是各种的图表,毕竟可视化需求一般都是由地图、饼图、柱状图、折线图等之类的各种图表组成。也就是将各种数据组合拆解成直观的通过大屏幕展示各项数据信息,简而言之就是数据可视化大屏项目。那大家肯定多多少少也参与或接触过可视化大屏项目,在项目中,那我么作为开发者来说更多关注的大多都是布局问题图表的兼容性问题窗口变化后图表样式问题,还有什么(???)补充一下

2022年完成的最后一个项目,嗯没错,就是数据可视化大屏项目,其实在这之前我有了解过大屏项目,却没有亲自实践过。当时激动的心颤抖的手,我来和你喝杯酒,呵tui,跑题了,这个年还是有味的(清香型)

hetui.png

事实呢,可把我搞的头破血流,每天都是什么鬼,怎么会这样,哎呦我去,怎么又出问题了,快被干趴了。哎呀我服,我服了,大师快来救救我。

大师:

大屏干.png

需求明述

那到底什么问题把我搞成这样呢,那就来讲讲是什么需求实现起来让我疯狂。

  • 分为左右两屏,左右两屏的数据需要通信,右侧大屏数据需要根据左侧大屏选择不同的内容展示相应的数据
  • 右侧大屏页面还有跳转页面的逻辑,根据左屏某些操作控制

通俗点说就是两个浏览器之间通信吗。很简单😆😆是吧

解决方案

跨页面通信呢,实现方式还是比较多的,那我也不列举了

大屏我也不知道.png

就说一下我的实现方式:是大家经常会用到的方式

那就是localStorage了,当然跨域就不适用了,emmmm

就是这个跨域,咱也不清楚为什么,当时这个项目公司接到了,但是呢,甲方把项目分成了两部分,还有另一公司在负责另一部分。这都没问题,毕竟人家甲方吗。问题来了,交接完成之后,甲方把这来两部分分别部署在了两个ip(Don't know why),五雷轰顶,两方的数据通信顿时垮了,怎么办,大师说还得继续干,哎加了好几晚的班

实现步骤

刚开始呢,我的想法很简单没有太多的逻辑,不就是左屏页面发送消息,右屏页面接收消息

项目的开发框架Vue3

第一版

来看下第一版实现,当时想法也比较简单,待我翻一翻代码提交记录😡

image-20230202161826248.png

映入眼帘的都是流泪的日子,天天修复,没完没了

看看左右屏页面怎么发送与接收消息的

左屏页面发送消息

localStorage.setItem('页面路径', JSON.stringify('参数信息'))

右瓶页面接收消息

window.addEventListener('storage', function (event) {
        console.log(event);
        localStorage.getItem('event.key')
        window.open('')
})

这写法看着中规中矩的,但是过于简单了点,也太不够格了。

事实是页面还是比较多的,总不能满眼都是localStorage

第二版

除了localStorage还需要改什么呢,之前说了右侧页面是需要根据左侧跳转的,那也用到了keep-alive保存页面状态,那大家这个时候应该都会想到vue的钩子函数吧onActivated,是的,必须要用到它,在页面切换时,需要接着渲染数据并且重新让右侧屏幕监听数据通信

const WebStorage = {
  set: (key: string, value: any) => {
    localStorage.setItem(key, JSON.stringify(value));
  },
  get: (key: string) => {
    const val = localStorage.getItem(key);
    if (val) {
      return JSON.parse(val);
    }
    return '';
  },
  remove: (key: string) => {
    localStorage.removeItem(key);
  },
};

左屏页面发送消息

 WebStorage.set('页面路径', '参数信息');

右屏页面接收消息

onActivated(() => {
  window.addEventListener('storage', function (event) {
      console.log(event);
      WebStorage.get('event.key')
      window.open('')
  })
}

完善需求问题优化

第二版其实大概的实现方向已经完成了,数据通信也没问题,剩下的就是往里填充一些逻辑了。

那我们在想想,还有哪些问题需要优化完善

  1. 当页面组件被切换移出keep-alive时,还需要清掉window.addEventListener('storage',()=>{}),需要用到onDeactivated
  2. 还有个需求,当右屏没被打开时,左屏时可以操作直接打开右屏的,关闭右屏左屏不受影响
  3. 关闭网页时,需要清空所有存储信息,防止对下次进入网页有影响
  4. 两个浏览器tabs在切换窗口后失活问题

之前localStorage.setItem('页面路径','')已页面路径作为参数的原因呢,一是因为页面比较多,二是因为切换页面通过onActivated激活后,方便通过当前页面的路径作为参数继续监听数据通信

问题解决方案

那我们按照上边几点内容逐个解决下

第一点

只需要在移出时,移除对storage的监听事件

onDeactivated(() => {
    window.removeEventListener('storage', ()=>{});
});

第二点

就是需要来判断下右屏的状态

既然要控制右屏,那最好还是用单独的一个localstorage来存储右屏状态

给定义个名称SCREEN-RIGHT-STATUS

在操作打开右屏时判断下右屏状态,已经打开的话,是不需要在新开窗口的

if (!WebStorage.get(SCREEN-RIGHT-STATUS)) {
      window.open(obj.url);
}

那我们在关闭右屏时呢,只需给它移除

WebStorage.remove(SCREEN-RIGHT-STATUS);

第三点

就是清空所有存储吗

WebStorage.remove(xxxx);

第四点

需要在页面上解决窗口失活问题

window.document.addEventListener('visibilitychange', () => {
      if (window.document.visibilityState === 'visible') {
        // 做一些数据的获取
      }
});

最终版

以上几点内容解决后呢,只需要将它们填充在适当位置,放在对应的页面就可以了,但是呢,体现不出编码能力

最后还是给它来整理一下整成一个hooks

import { onDeactivated } from 'vue';
​
// 上边优化的localStorage文件
import WebStorage from '/storage';
​
// 初始页面的路径
let originalUrl = '';
​
// 右侧屏幕状态管理器
const SCREEN_RIGHT_SHOW = 'SCREEN-RIGHT-STATUS';
​
/**
 * @param {*} bridgeName 存储状态的KEY值
 * @param {*} hasDestroy 是否需要在移除组件时移除监听
 * @returns
 */
export function useScreen(bridgeName = 'SCREEN-INTERACTION', hasDestroy = true) {
  let listenStoreCb;
​
  // 设置存储信息
  function setScreenBridgeValue(obj) {
    // obj为跳转页面的路径以及其它参数
    // !WebStorage.get(SCREEN_RIGHT_SHOW) 判断右屏是否存在
    if (obj.url && !WebStorage.get(SCREEN_RIGHT_SHOW)) {
      window.open(obj.url);
    }
    WebStorage.set(bridgeName, obj);
  }
​
  // 获取存储内容
  function getScreenBridgeValue() {
    return WebStorage.get(bridgeName);
  }
​
  // 清除存储内容
  function clearScreenBridge() {
    WebStorage.remove(bridgeName);
  }
​
  // 监听storage动态
  function listenScreenBridge(fn, cb?) {
    const back = () => {
      const data = getScreenBridgeValue();
      if (data && data.url) {
        // 跳转新页面
        if (originalUrl !== data.url) {
          // 将路径回传
          fn && fn(data.url);
          originalUrl = data.url;
        }
        if (data.data) {
          // 存在其他参数,将其他参数回传
          cb && cb(data.data);
        }
      } else {
        window.close();
      }
    };
​
    listenStoreCb = back;
    window.addEventListener('storage', listenStoreCb);
​
    //解决window在切换窗口后失活问题
    window.document.addEventListener('visibilitychange', () => {
      if (window.document.visibilityState === 'visible') {
        listenStoreCb();
      }
    });
  }
​
  // 页面跳转
  function jumpHref(url) {
    window.location.href = url;
  }
​
  // 右屏页面方法
  function screenDestroy() {
    WebStorage.set(SCREEN_RIGHT_SHOW, true);
    window.onbeforeunload = function () {
      WebStorage.remove(SCREEN_RIGHT_SHOW);
    };
  }
​
  window.onbeforeunload = function () {
    setTimeout(() => {
      clearScreenBridge();
    }, 300);
  };
​
  // 组件移除时出发
  onDeactivated(() => {
    if (listenStoreCb && hasDestroy) {
      window.removeEventListener('storage', listenStoreCb);
    }
  });
​
  return {
    setScreenBridgeValue,
    getScreenBridgeValue,
    listenScreenBridge,
    clearScreenBridge,
    jumpHref,
    screenDestroy,
  };
}

主要说明

Key:SCREEN_RIGHT_SHOW; 维护右屏的状态管理

key:SCREEN-INTERACTION; 维护左右两屏的数据通信

使用方法

那最后如何使用呢,我们先看下左右两屏分别需要做的事情

左屏幕需要做的事情

  • 打开右侧屏幕
  • 向右侧屏幕发送数据
const { setScreenBridgeValue } = useScreen();
​
setScreenBridgeValue({
      url: '页面路径',
      data: { xxxxxx:'xxxxxx' },
});
​
// 打开右侧屏幕在setScreenBridgeValue() 方法内部已处理

右屏幕需要做的事情

  • 接收左屏幕传输的数据
  • 根据url跳转相应的页面
  • 监听存储数据变化
  • 组件移出时移除监听
onActivated(() => {
    // 获取存储书记处
    const res = getScreenBridgeValue();
    // 对数据根据需求逻辑做些处理
    // xxxxxxxxxx
    
    //监听storage更新
    listenScreenBridge(
      (url) => {
        //跳转相应页面
        jumpHref(url);
      },
      (data) => {
        // 对数据变化后需求逻辑做些处理
        // xxxxxxxxxx
      },
    );
    
    // 右屏关闭时清除右屏状态
    screenDestroy();
  });

至于上述碰到的问题localStorage跨域通信

我改用的解决方式是通过url参数的形式进行了数据的传输,只需要在有需求的地方单独处理这种传输方式

不过应该很大概率不会碰到这个问题

结语

如果感觉此文的大屏数据交互方式对你帮助的话,请不吝点个赞🥺🥺🥺,支持一下

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情