导入个Excel页面直接卡死,看我如何处理T0生产事故~

模拟面试、简历指导、入职指导、项目指导、答疑解惑可私信找我~已帮助100+名同学完成改造!

前言

大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心~

背景

我们都知道JavaScript是单线程的语言,也就是说同步代码都需要排队去执行,这样就会造成很多问题,就比如今天讲的:数据量大的excel文件的导入导出,会造成整个页面出现“假卡死”的现象。

什么是“假卡死”呢?意思就是页面其实并没有卡死,但是用户做一些操作时,页面并没有及时给到反馈,这就会让用户觉得页面卡死了~

数据量大的excel

数据量大的excel文件的导入导出,就会造成这个问题,原因是当我们使用库去解析数据量大的excel时,是非常耗时的,这就导致了后面的代码执行不了,所以出现“假卡死”现象

我们可以通过一个小案例来体现出这个问题,我们先准备一个 excel 文件,它差不多有 1w 行数据

接着我们在项目中去装xlsx这个插件,用于解析待会导入的excel文件

pnpm i xlsx

接着准备一个上传的input,和一个按钮button,并准备他们对应的事件

<input @change="handleImport" type="file" />
<button type="button" @click="handleClick">按钮</button>
const handleImport = (ev: Event) => {
  const file = (ev.target as HTMLInputElement).files![0];

  // 使用FileReader读取文件内容
  const reader = new FileReader();

  reader.onload = function (e: any{
    const data = new Uint8Array(e.target.result);
    console.time('解析excel');
    // 使用SheetJS的XLSX库解析文件数据
    const workbook = XLSX.read(data, { type'array' });
    console.timeEnd('解析excel');
    // 获取工作表对象
    const firstSheet = workbook.Sheets[workbook.SheetNames[0]];

    // 解析工作表数据
    const jsonData = XLSX.utils.sheet_to_json(firstSheet, { header: 1 });

    // 处理解析后的数据
    // console.log(jsonData);
  };

  // 读取文件
  reader.readAsArrayBuffer(file);
};
const handleClick = () => {
  console.log('打印');
};

这个时候,我们去导入excel文件,然后马上去点击按钮,期望的输出顺序肯定是

-> 打印
-> 解析excel

因为解析excel是需要时间的,差不多要2-3s,在这2-3s中去点击按钮,应该先让按钮点击的事件先执行,但是事实上效果不是这样的,我这里其实在上传过程中点了很多次按钮了,但是就是不打印,而是等到excel解析完之后,才会打印,这就像卡死了一样,明明我点了按钮,但就是没反应

WebWorker解决导入

这个问题可以用 WebWorker 来解决,WebWorker 能开启一个子线程,来做一些我们需要让它做的事情,并把结果返回到主线程,这样就可以不阻塞主线程的代码执行了~

WebWorker VS setTimeout

有人就问了,为啥要使用 WebWorker,而不是 setTimout 呢?不是照样也可以吗?

  • 其实在单核电脑上,它两确实没啥区别,但是在多核电脑上,WebWorker 可以同时使用多个cpu来计算, 速度比 setTimeout快~
  • setTimeout 还是在主线程上执行,还是会占主线程的资源影响主线程的执行,但是 WebWorker 跟主线程是隔离开的

代码 & 效果

const handleImport = (ev: Event) => {
  const file = (ev.target as HTMLInputElement).files![0];
  const worker = new Worker(
    URL.createObjectURL(
      new Blob([
        `
        importScripts('https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.16.4/xlsx.full.min.js');
        onmessage = function(e) {
          const fileData = e.data;
          const workbook = XLSX.read(fileData, { type: 'array' });
          const sheetName = workbook.SheetNames[0];
          const sheet = workbook.Sheets[sheetName];
          const data = XLSX.utils.sheet_to_json(sheet, { header: 1 });
          postMessage(data);
        };
        `
,
      ]),
    ),
  );

  // 使用FileReader读取文件内容
  const reader = new FileReader();

  reader.onload = function (e: any{
    const data = new Uint8Array(e.target.result);
    worker.postMessage(data);
  };

  // 读取文件
  reader.readAsArrayBuffer(file);

  // 监听Web Worker返回的消息
  worker.onmessage = function (e{
    console.log('解析完成', e.data);
    worker.terminate(); // 当任务完成后,终止Web Worker
  };
};

我们可以看到,在解析excel的过程中,点击按钮的事件并不会被阻塞到了~

WebWorker解决导出

道理都是一样的,就是利用 WebWorker 来帮我们做一些比较耗时的事情

const handleExport = () => {
  const worker = new Worker(
    URL.createObjectURL(
      new Blob([
        `
        importScripts('https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.16.4/xlsx.full.min.js');
        onmessage = function(e) {
          const data = e.data;
          const workbook = XLSX.utils.book_new();
          const sheetData = e.data
          const worksheet = XLSX.utils.aoa_to_sheet(sheetData);
          XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
          const excelData = XLSX.write(workbook, { type: 'array', bookType: 'xlsx' });
          postMessage(excelData, [excelData.buffer]);
        };
        `
,
      ]),
    ),
  );

  worker.onmessage = function (e{
    const excelData = e.data;

    // 创建 Blob 对象,用于保存 Excel 文件数据
    const blob = new Blob([excelData], {
      type'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    });

    // 创建下载链接并触发下载
    const downloadLink = document.createElement('a');
    downloadLink.href = URL.createObjectURL(blob);
    downloadLink.download = 'export.xlsx';
    downloadLink.click();

    // 终止 Web Worker
    worker.terminate();
  };

  worker.postMessage([...你的数据]);
};

结语

我是林三心

  • 一个待过小型toG型外包公司、大型外包公司、小公司、潜力型创业公司、大公司的作死型前端选手;
  • 一个偏前端的全干工程师;
  • 一个不正经的掘金作者;
  • 逗比的B站up主;
  • 不帅的小红书博主;
  • 喜欢打铁的篮球菜鸟;
  • 喜欢历史的乏味少年;
  • 喜欢rap的五音不全弱鸡如果你想一起学习前端,一起摸鱼,一起研究简历优化,一起研究面试进步,一起交流历史音乐篮球rap,可以来俺的摸鱼学习群哈哈,点这个,有7000多名前端小伙伴在等着一起学习哦 --> 

广州的兄弟可以约饭哦,或者约球~我负责打铁,你负责进球,谢谢~


相关推荐

  • 一篇文章让你搞懂到底什么是 CDN
  • select...for update 锁表了?
  • 建议前端开发者学习下色彩心理学,提升用户体验
  • 开发过程中,建议使用 VSCode 的 Thunder Client 插件替代 Postman, 让你显得更专业
  • Mybatis的一级缓存与二级缓存
  • 丰富的模板与插件,构建你心中的理想站点
  • 陈怡然力荐《关于我博士毕业的这件小事》,Waymo研究员2年半心路分享火了
  • 大华股份发布星汉大模型;苹果AI服务器支出明年或达47.5亿美元;英伟达H100成新型债务资产丨AIGC大事日报
  • 净利润暴涨1763%,世界第三大软件公司如何靠AIGC逆风翻盘?
  • 提升时间序列聚类的表现的秘密方法。
  • 语雀停服八小时,P0级事故!故障原因和补偿来了!!
  • SpringBoot 快速实现 api 加密,一个轮子搞定!
  • [开源]一套企业级微服务框架、微服务能力开放平台,功能强大
  • 低风险、高回报,AI+简历修改项目分享
  • 1024 程序员节|100 秒里的小红书技术这一年
  • 30.4K Star开源项目:探索二进制世界的强大十六进制编辑器
  • 思路打开,换个方式解决算力和数据问题
  • Python网页开发神器fac新版本来了
  • 闹大了!淘宝这是什么操作?!
  • 清华新研究解密信息茧房!全新信息动力学理论,登Nature子刊