将大数据从 Web Worker 传输到 Vue.js 中的主线程

问题描述 投票:0回答:1

如何有效地将 Web Worker 中生成的大型数据集(可能超过一百万个项目)传输到 Vue.js 应用程序的主线程中,我面临着挑战。目前,当我尝试将生成的网格从工作线程传递到主线程时,会导致诸如“超出最大调用堆栈大小”之类的错误,甚至导致页面崩溃。

这是我当前的 Web Worker 代码的简化版本:

// gridWorker.js
self.onmessage = function (e) {
  const { isUpdateColor, row, col, cellSize } = e.data;

  if (isUpdateColor) {
    const grid = Array.from({ length: row }, (_, i) => {
      return Array.from({ length: col }, (_, j) => {
        return {
          i,
          j,
          code: `spot_${i}_${j}`,
          // ... other properties
        };
      });
    });

    self.postMessage({ grid });
  }
};

//main thread
<v-stage>
    <v-layer ref="pathFinderLayer">
        <div v-if="showTravelDistancePage">
          <template v-for="rows in grid">
            <v-rect v-for="(item, index) in rows" :key="item.code + '-' + index" :config="item"
              @click="gridClick(item)" />
          </template>

        </div>
      </v-layer>
</v-stage>


methods:
prepareGrid(isUpdateColor,cleanPath=true) {
    this.gridWorker = new GridWorker();
      this.gridWorker.onmessage = (event) => {
        const { grid } = event.data;
          this.grid = grid;
        }

      };
}

我怀疑问题是由于尝试将如此大的数据集传递到主线程而引起的。有没有更有效的方法来处理这种情况?即使我逐块传递部分数据,如果网格对象太多,它仍然会使我的页面崩溃。

方法一:在主线程上生成

最初,我尝试直接在主线程上生成大网格。然而,由于对象数量巨大,这种方法会导致页面崩溃。

方法 2:在 Web Worker 中生成并传递整个网格

为了解决性能问题,我将网格生成过程移至 Web Worker。虽然这成功地将生成卸载到单独的线程,但将整个生成的网格传递回主线程仍然会导致页面崩溃。

方法 3:通过 Chunk 在 Web Worker Chunk 中生成

在最近的尝试中,我选择在 Web Worker 中分块生成网格。然后,我将每个块单独传递到主线程,将块存储在主网格内。不幸的是,这个策略在某个时候也导致了崩溃。

vue.js web-worker konvajs konva vue-konva
1个回答
0
投票

似乎是 XY 问题。简短的回答 - 您不应该做诸如生成一百万项 UI 数据之类的事情。 您应该只生成屏幕上可见的数据,这称为虚拟滚动。 因此,您可以在需要时动态生成数据。

如果您仍需要提前生成 100 万个项目,请在工作线程中执行此操作,并仅在滚动时传输可见数据。

一个带有简单虚拟列表的示例(它甚至可以更好地滚动)。正如你所看到的,我动态生成 HTML(更高级的滚动也会偏移顶部项目,模仿项目内的滚动):

const arr = Array.from({length:1000000}, () => Math.random());

unorderedList.style.height = (arr.length * 32) + 'px';

drawItems();

wrapper.addEventListener('scroll', drawItems);

function drawItems(){
  const pos = wrapper.scrollTop;
  const from = pos / 32 | 0;
  const to = from + wrapper.offsetHeight / 32 | 0 + 1;
  unorderedList.innerHTML = arr.slice(from, to).map((item, idx) => `<li>${from + idx + 1}: ${item}</li>`).join('');
  let top = -32;
  [...unorderedList.children].forEach((li, idx) => li.style.top = `${top += 32}px`);
}
#wrapper{
  max-height:100vh;
  overflow:auto;
}
body{
margin:0;
padding:0;
overflow:hidden;
}
li{
 position:absolute;
 height:32px;
 line-hieght:32px;
}
<div id="wrapper">
<ul id="unorderedList"></ul>
</div>

© www.soinside.com 2019 - 2024. All rights reserved.