如何在vue项目中使用Iframe使得任务分区!

·  阅读 366

如何在vue项目中使用Iframe使得任务分区!

一、问题描述

背景:前两天,我和我的同事遇到一个问题,大概是这样子的,在一个项目的一个页面中使用websocket获取后台的实时数据,实时数据每一秒钟得到一条,一条数据大约有12组(可能会更多),前端需要分组将其动态的绘制到12个echarts图表中;画出来的图是基于这个图例的,也就是说,每一秒钟浏览器都需要绘制超过12个这样的图例,项目设计上是没有做分页或是其他的分页处理的;因此只能硬着头皮同时绘制在一个页面中;

Snipaste_2022-01-18_10-15-37.png

问题:问题在于,因为需求是想要看到从业务开始到业务结束的所有数据,因此socket的数据,是一直缓存在前端的;并且每一秒钟都需要进行一次绘制,所以随着时间的累计,前端的数据会是一个比较大的增量,并且随着时间的累计绘制的消耗也会越来越大,当我们绘制到第2分钟的时候,就会出现菜单卡顿的问题;

分析:卡顿的问题其实主要是因为在每一秒钟绘制图标的过程中,本质上是js在操作canvas和执行计算的过程,所以是js线程的任务过大,阻塞了浏览器渲染线程的原因,导致浏览器无法在16ms以内有盈余去执行渲染,使得菜单的点击或是其他的操作看起来卡顿,不流畅;

解决思路:有这样几种解决思路,第一可以分页处理,让前端缓存的数据,不要那么多,并且较少绘制的量级,比如实时绘制几个图例就好了,说白了就是任务分时;第二是因为这个业务场景是单页面应用,所以整个项目只有一个HTML,因此可以借助iframe,在vue中嵌套一个iframe,让绘制的任务在这个Iframe中执行,哪怕这个里面的任务再多,也不会影响主应用的渲染,因为他们是两个独立的域,只要解决通信问题,便能够使得我们的需求得到比较好的实现;

二、具体实现

1、文件准备

在进行实现之前,我做了一个小的Demo,初步进行了一个实验;

首先在自己项目当中的public文件夹中创建一个iframe.html,在这里面之后会写主要的绘制任务;

node_modules
|
public
|  |
|  iframe.html
|  |
|  index.html
复制代码

然后创建如下的vue文件;

<template>
  <div>
    <h1>iframe</h1>
    <button @click="move">移动</button>
    <div class="div" :style="{transform:`translateX(${distence}px)`}"></div>
    <iframe :src="src" frameborder="0"></iframe>
  </div>
</template>

<script>
export default {
  name:"Iframe",
  data(){
    return {
      src:"http://localhost:8081/iframe.html",
      distence:10
    }
  },
  methods:{
    move(){
      this.distence +=100;
    }
  }
}
</script>

<style>
.div{
  width:100px;height:100px;background-color:red;
  position:relative;
  transition:all 0.5s linear;
}
</style>
复制代码

2、问题解释

我想解释一下,为什么这里的src要使用url而不使用相对路径或者绝对路径,因为iframe的src属性必须满足一个点就是src指向的必须是一个独立的域,因为iframe标签最终是要经过编译打包的,无论在开发环境还是生产环境,因此打包之后,在开发环境即便指向的是一个你定义好的新的HTML,但是编译之后,全都指向了当前index.html,所以会出现iframe的内容就是index.html的内容的问题;

因此我们需要将src指向一个url,直接访问到public中iframe.html的内容,这样做就可以生效;

通过这样的方式,运行这个项目应该就可以使用了;可以看到iframe的位置就是iframe.html里面的内容;

Snipaste_2022-01-18_10-44-57.png

3、iframe任务

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.2.2/echarts.common.js"></script>
    <style>
        #echart {
            width: 300px;
            height: 300px;
            border: 1px solid #ccc;
        }
        
        .move {
            width: 100px;
            height: 100px;
            background: red;
        }
    </style>
</head>

<body>
    <div id="echart"></div>
    <script>
        let bar = echarts.init(document.getElementById("echart"));
        let option = {
            xAxis: {
                type: 'category',
                data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
            },
            yAxis: {
                type: 'value'
            },
            series: [{
                data: [150, 230, 224, 218, 135, 147, 260],
                type: 'line'
            }]
        };
        bar.setOption(option);
        let genData = () => {
            let arr = [];
            for (let i = 0; i < 10000; i++) {
                arr.push(Math.ceil(Math.random() * 100))
            }
            return arr
        }
        setInterval(() => {
            bar.setOption({
                series: {
                    data: genData()
                }
            })
        }, 1000)
    </script>
</body>

</html>
复制代码

4、iframe任务

在iframe引入ecahrts的cdn,然后进行绘制,可以看到,我设置的是每秒钟绘制10000条数据;可以说这个绘制的消耗还是有挑战的,但是在vue文件中去执行一个动画,移动方块;可以看到其实是并不阻塞的,因为他们完全就是在不同的域

微信图片_20220118113320.gif

三、总结

以上还有其他的方案,在这里就不进行一一详述了,如果有理解不合理的地方请读者指正,万分感激!

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改