前端优化 之WebWorker

81 阅读3分钟

js是单线程的 一些计算量大的数据放在前端一般情况下会造成两种情况

  • 页面卡死
  • 在计算的过程中用户操作页面是无响应的

Worker

Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。

Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭

Worker.postMessage()

Worker 接口的 postMessage()  方法可以向 worker 发送消息。第一个参数是要发送到 worker 的数据。该数据可以是任何可以被结构化克隆算法处理的 JavaScript 对象。

Worker 的 postMessage()  方法委托给 MessagePort 的 postMessage() (en-US) 方法,该方法会在对应的用于接收 MessagePort 的事件循环中添加一个任务。

Worker.terminate()

Worker 接口中的 terminate()  方法用于立即终止 Worker 的行为。本方法并不会等待 worker 去完成它剩余的操作;worker 将会被立刻停止。

Worker:message 事件

当 worker 的父级接收到来自其 worker 的消息时(也就是说,当 worker 通过 DedicatedWorkerGlobalScope.postMessage() (en-US) 发送消息时),会在 Worker 对象上触发 message 事件。

此事件不能取消,也不会冒泡。

Demo

<script setup>
import { ref } from "vue";
import { xlsx } from "./../utils/xlsx.js";
const val = ref("");

const listHander = ref({
  id: "学号",
  name: "姓名",
  age: "年龄",
  classes: "学院",
});
const jsonData = ref([]);
for (let i = 0; i < 500000; i++) {
  jsonData.value.push({
    id: i,
    name: "小智" + i,
    age: i,
    classes: "商学院",
  });
}
const exportHandler = () => {
  console.log("导出");
  xlsx(jsonData.value, listHander.value, "study");
};
</script>

<template>
  <input v-model="val" />
  <button @click="exportHandler">导出</button>
</template>

<style scoped></style>

  • 循环生成50万条数据
  • 定义了一个导出事件 exportHandler

虽然效果已达预期 可以正确导出文件 但是点击 导出按钮 在导出文件的过程中 输入框乃至整个页面 操作是无响应的

接下来使用worker优化

const xlsxWorker = new Worker("http://localhost:5173/exportXlsx.js");


xlsxWorker.onmessage = (e) => {
  const workerBook = e.data
  writeFile(workerBook,"ttt.xlsx")
  xlsxWorker.terminate()
};


const exportHandler = () => {
  xlsxWorker.postMessage("开始下载")
};

exportXlsx.js


importScripts('./xlsx.js')

const jsonData = [];
for (let i = 0; i < 1000000; i++) {
    jsonData.push({
        id: i,
        name: "小智" + i,
        age: i,
        classes: "商学院",
    });
}

const listHander = {
    id: "学号",
    name: "姓名",
    age: "年龄",
    classes: "学院",
};

console.log(XLSX);

self.onmessage = function () {
    const sheet = XLSX.utils.json_to_sheet(jsonData);
    const workbook = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(workbook, sheet, 'sheet1');
    self.postMessage(workbook)
}

XLSX对象打印 image.png

整体思路

  1. new Worker()开启一个线程 计算类交给线程去做
  2. 用户触发后 使用postMessage向线程传递信息
  3. 线程通过onmessage监听到并计算后 将结果通过postMessage向主线程传递
  4. 主线程通过onmessage接收到后进行操作

结果 下载166MB的文件时 页面无卡顿 下载过程中用户也可进行其他操作 全程相当于后台执行

image.png