模拟面试、简历指导、入职指导、项目指导、答疑解惑可私信找我~已帮助100+名同学完成改造!
大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心~
我们都知道JavaScript是单线程的语言,也就是说同步代码都需要排队去执行,这样就会造成很多问题,就比如今天讲的:数据量大的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,而不是 setTimout 呢?不是照样也可以吗?
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 来帮我们做一些比较耗时的事情
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([...你的数据]);
};
我是林三心
广州的兄弟可以约饭哦,或者约球~我负责打铁,你负责进球,谢谢~