当前位置: 首页 > news >正文

记录--前端使用a链接下载内容增加loading效果

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

问题描述:最近工作中出现一个需求,纯前端下载 Excel 数据,并且有的下载内容很多,这时需要给下载增加一个 loading 效果。

代码如下:

// utils.js
const XLSX = require('xlsx')
// 将一个sheet转成最终的excel文件的blob对象,然后利用URL.createObjectURL下载
export const sheet2blob = (sheet, sheetName) => {sheetName = sheetName || 'sheet1'var workbook = {SheetNames: [sheetName],Sheets: {}}workbook.Sheets[sheetName] = sheet// 生成excel的配置项var wopts = {bookType: 'xlsx', // 要生成的文件类型bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性type: 'binary'}var wbout = XLSX.write(workbook, wopts)var blob = new Blob([s2ab(wbout)], { type: 'application/octet-stream' })// 字符串转ArrayBufferfunction s2ab(s) {var buf = new ArrayBuffer(s.length)var view = new Uint8Array(buf)for (var i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xffreturn buf}return blob
}/*** 通用的打开下载对话框方法,没有测试过具体兼容性* @param url 下载地址,也可以是一个blob对象,必选* @param saveName 保存文件名,可选*/
export const openDownloadDialog = (url, saveName) => {if (typeof url === 'object' && url instanceof Blob) {url = URL.createObjectURL(url) // 创建blob地址}var aLink = document.createElement('a')aLink.href = urlaLink.download = saveName + '.xlsx' || '1.xlsx' // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效var eventif (window.MouseEvent) event = new MouseEvent('click')else {event = document.createEvent('MouseEvents')event.initMouseEvent('click',true,false,window,0,0,0,0,0,false,false,false,false,0,null)}aLink.dispatchEvent(event)
}<el-button@click="clickExportBtn"
><i class="el-icon-download"></i>下载数据
</el-button>
<div class="mongolia" v-if="loadingSummaryData"><el-icon class="el-icon-loading loading-icon"><Loading /></el-icon><p>loading...</p>
</div>clickExportBtn: _.throttle(async function() {const downloadDatas = []const summaryDataForDownloads = this.optimizeHPPCDownload(this.summaryDataForDownloads)summaryDataForDownloads.map(summaryItem =>downloadDatas.push(this.parseSummaryDataToBlobData(summaryItem)))//  donwloadDatas 数组是一个三维数组,而 json2sheet 需要的数据是一个二维数组this.loadingSummaryData = trueconst downloadBlob = aoa2sheet(downloadDatas.flat(1))openDownloadDialog(downloadBlob, `${this.testItem}报告数据`)this.loadingSummaryData = false
}, 2000),// css
.mongolia {position: fixed;top: 0;left: 0;right: 0;bottom: 0;background-color: rgba(0, 0, 0, 0.9);display: flex;justify-content: center;align-items: center;font-size: 1.5rem;color: #409eff;z-index: 9999;
}
.loading-icon {color: #409eff;font-size: 32px;
}

解决方案探究:

  • 在尝试了使用 $nextTick、将 openDownloadDialog 改写成 Promise 异步函数,或者使用 async/await、在 openDownloadDialog 中添加 loadingSummaryData 逻辑,发现依旧无法解决问题,因此怀疑是 document 添加新元素与 vue 的 v-if 渲染产生冲突,即 document 添加新元素会阻塞 v-if 的执性。查阅资料发现,问题可能有以下几种:

    • openDownloadDialog 在执行过程中执行了较为耗时的同步操作,阻塞了主线程,导致了页面渲染的停滞。

    • openDownloadDialog 的 click 事件出发逻辑存在问题,阻塞了事件循环(Event Loop)。

    • 浏览器在执行 openDownloadDialog 时,将其脚本任务的优先级设置得较高,导致占用主线程时间片,推迟了其他渲染任务。

    • Vue 的批量更新策略导致了 v-if 内容的显示被延迟。

  • 查阅资料后找到了如下几种方案:

1.使用 setTimeout 使 openDownloadDialog 异步执行

clickExport() {this.loadingSummaryData = true;setTimeout(() => {openDownloadDialog(downloadBlob, `${this.testItem}报告数据`);this.loadingSummaryData = false;});
}

2.对 openDownloadDialog 内部进行优化

  • 避免大循环或递归逻辑

  • 将计算工作分批进行

  • 使用 Web Worker 隔离耗时任务

  在编写 downloadWorker.js 中的代码时,要明确这部分代码是运行在一个独立的 Worker 线程内部,而不是主线程中。

  1. 不要直接依赖或者访问主线程的全局对象,比如 window、document 等。这些在 Worker 内都无法直接使用。

  2. 不要依赖 DOM 操作,比如获取某个 DOM 元素。Worker 线程无法访问页面的 DOM。

  3. 代码执行的入口是 onmessage 回调函数,在其中编写业务逻辑。

  4. 和主线程的通信只能通过 postMessage 和 onmessage 发送消息事件。

  5. 代码应该是自包含的,不依赖外部变量或状态。

  6. 可以导入其他脚本依赖,比如用 import 引入工具函数等。

  7. 避免修改或依赖全局作用域,比如定义全局变量等。

总之,编写 downloadWorker.js 时要注意它执行在一个独立环境中,只能通过消息事件与主线程通信。

// 创建 Worker
const worker = new Worker('downloadWorker.js'); // 点击下载时向 Worker 发送消息
function clickDownload() {showLoading();worker.postMessage({url: fileURL,filename: 'report.xlsx'});worker.onmessage = function(e) {// 收到下载完成的消息hideLoading();}}// 显示 loading
function showLoading() {loading.style.display = 'block'; 
}// 隐藏 loading
function hideLoading() {loading.style.display = 'none';
}// downloadWorker.jsonmessage = function(e) {const url = e.data.url;const filename = e.data.filename;// 创建并点击链接触发下载const a = document.createElement('a');a.href = url;a.download = filename;a.click();postMessage('下载完成');
}<div id="loading" style="display:none;">Downloading...
</div><button onclick="clickDownload()">Download
</button><script src="downloadWorker.js"></script>

3.使用 requestIdleCallback 进行调度

clickExport() {this.loadingSummaryData = true;requestIdleCallback(() => {openDownloadDialog(downloadBlob, `${this.testItem}报告数据`);this.loadingSummaryData = false; });
}

当使用上述方案时发现里面的内容变得异常的慢,于是去问GPT后,得到以下答案:

使用 requestIdleCallback 来执行 openDownloadDialog 确实可能会需要更多时间。

因为 requestIdleCallback 会在主线程空闲时才执行回调函数,所以不像 setTimeout 可以直接按指定时间异步执行。

requestIdleCallback 的优先级较低,只在主线程空闲时才会执行,所以 download 的速度会比 setTimeout 慢一些。

但使用 requestIdleCallback 的优点是它不会占用主线程时间,可以避免阻塞页面渲染。

所以需要权衡执行速度和避免阻塞之间的关系:

  • 如果下载速度是优先考虑的因素,可以继续使用 setTimeout 异步执行。

  • 如果避免阻塞渲染更重要,则 requestIdleCallback 是更好的选择。

偶尔下载任务被延迟不是大问题,但频繁的下载任务阻塞会严重影响页面体验。

此外,可以结合两者试试:

clickExport() {this.loadingSummaryData = true;setTimeout(() => {requestIdleCallback(() => {openDownloadDialog(downloadBlob);});this.loadingSummaryData = false;}, 200);}

setTimeout 先异步调度,然后在回调中使用 requestIdleCallback 避免阻塞。这样就可以兼顾下载速度和避免渲染阻塞了。

分析线程模型,找到具体的阻塞点

使用 Performance 工具分析线程

debugger 及 console.log 打印关键函数的执行时间

检查是否有非主线程的任务阻塞了主线程

调整 vue 的批量更新策略

new Vue({config: { // 修改批量更新的队列长度batchUpdateDuration: 100 }
})

本文转载于:

https://juejin.cn/post/7268050036474609683

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

相关文章:

记录--前端使用a链接下载内容增加loading效果

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 问题描述&#xff1a;最近工作中出现一个需求&#xff0c;纯前端下载 Excel 数据&#xff0c;并且有的下载内容很多&#xff0c;这时需要给下载增加一个 loading 效果。 代码如下&#xff1a; // util…...

如何获取用户的ip地址

用户的 IP 地址可能会被隐藏或者修改&#xff0c;例如使用代理服务器、VPN 等工具&#xff0c;这样就无法准确获取用户的真实 IP 地址。 除了以上特殊情况&#xff0c;一般情况下 用户访问可能会经过一下链路 &#xff1a; 前端—>nginx—>网关—>服务 。 一般情况下后…...

单片机-控制按键点亮LED灯

1、按键电路图 定义四个按键引脚 1、按键按下 为 输入为低电平 2、按键不按下 IO有上拉电阻&#xff0c;为高电平 // 定义 按键的 管教 sbit KEY1 P3^1; sbit KEY2 P3^0; sbit KEY3 P3^2; sbit KEY4 P3^3; 2、LED灯电路图 LED 输出高电平为亮 // 定义LED灯 管教 sbit LED1…...

微信小程序新版隐私协议弹窗实现最新版

1. 微信小程序又双叒叕更新了 2023.08.22更新&#xff1a; 以下指南中涉及的 getPrivacySetting、onNeedPrivacyAuthorization、requirePrivacyAuthorize 等接口目前可以正常接入调试。调试说明&#xff1a; 在 2023年9月15号之前&#xff0c;在 app.json 中配置 __usePriva…...

GO语言圣经 第五章习题

练习5.1 修改findlinks代码中遍历n.FirstChild链表的部分&#xff0c;将循环调用visit&#xff0c;改成递归调用。 func visit(links []string, n *html.Node) []string {if n nil {return links}if n.Type html.ElementNode && n.Data "a" {for _, a : r…...

用kotlin 开发一个简单的多页面跳转

本文介绍一个简单的安卓应用的页面跳转例子&#xff0c;用的是kotlin。 运行时主页面是一个hello 和Jump 按钮&#xff0c;你按一下jump 按钮就转到 从页面&#xff0c;只是标识从页面。 开始建立一个简单工程&#xff0c;名为hello&#xff0c; 选择的是Empty views Activit…...

记录我的tensorrt 部署yolov8

系统 &#xff1a;ubuntu 18.04 代码 &#xff1a;GitHub - noahmr/yolov5-tensorrt: Real-time object detection with YOLOv5 and TensorRT conda 环境 &#xff1a; GitHub - noahmr/yolov5-tensorrt: Real-time object detection with YOLOv5 and TensorRT cuda : 11.8 …...

什么是用户界面? 优漫动游

什么是用户界面&#xff1f; 用户界面(UI&#xff0c;UserInterface)也称人机界面&#xff0c;是人机交互、操作逻辑和界面表现的整体设计。每一种设计都有其对应的职业角色&#xff0c;其中&#xff0c;人机交互的设计人员叫做用户研究工程师&#xff0c;操作逻辑设计人员叫…...

基于 Docker 的 MySQL 主从复制搭建(Mac M1版本)

系统&#xff1a;Macbook M1 镜像版本&#xff1a;mysql:5.7 如果是要查 slave连接不上 master的问题&#xff0c;可以直接跳到文章末尾踩坑处 准备工作 拉取镜像 docker pull mysql:5.7本地数据卷挂载 因为mysql不挂载的话&#xff0c;重启丢失数据&#xff0c;所以在本地创…...

【Locomotor运动模块】瞬移

文章目录 一、原理二、两种类型1、Instant(立刻)2、Dash&#xff08;猛冲&#xff09; 三、瞬移区域、瞬移点1、瞬移区域2、瞬移点 一、原理 抛物线指针选择好目标位置&#xff0c;然后告诉瞬移预设体&#xff1a;你想法把游戏区域弄到目标位置来 解释&#xff1a;抛物线指针选…...

【负载均衡】常见的负载均衡策略有哪些?

文章目录 前言负载均衡分类常见负载均衡策略小结 前言 负载均衡策略是实现负载均衡器的关键&#xff0c;而负载均衡器又是分布式系统中不可或缺的重要组件。使用它有助于提高系统的整体性能、可用性、可靠性和安全性&#xff0c;同时支持系统的扩展和故障容忍性。对于处理大量…...

ChatGPT如何应对紧急救援和医疗应急?

ChatGPT在紧急救援和医疗应急方面具有潜在的重要用途。它可以用于提供信息、建议和支持&#xff0c;以帮助应对各种突发事件&#xff0c;如自然灾害、流行病爆发、事故等。以下是ChatGPT如何应对紧急救援和医疗应急的方式以及相关挑战的详细讨论。 ### 紧急救援 #### 1. 提供…...

vue3 ref reactive响应式数据 赋值的问题

文章目录 vue3 ref reactive响应式数据 赋值的问题场景1:将响应式数据赋值请求后的数据错误示范&#xff1a;直接赋值正确写法 场景2&#xff1a;响应式数据解构之后失去响应式原因分析解决办法 toRefs/toRef方法创建ref引用对象 vue3 ref reactive响应式数据 赋值的问题 doing…...

【美团秋招】20230922小美的彩虹糖

小美的彩虹糖 小美有很多的彩虹糖&#xff0c;每颗彩虹糖都有一个颜色&#xff0c;她每天可以吃两颗彩虹糖&#xff0c;如果今天吃的彩虹糖组合是之前没吃过的组合&#xff0c;则小美今天会很高兴。 例如&#xff0c;小美有 6 颗彩虹糖&#xff0c;颜色分别是 [1,1,4,5,1,4]。…...

论文阅读_扩散模型_DM

英文名称: Deep Unsupervised Learning using Nonequilibrium Thermodynamics 中文名称: 使用非平衡热力学原理的深度无监督学习 论文地址: http://arxiv.org/abs/1503.03585 代码地址: https://github.com/Sohl-Dickstein/Diffusion-Probabilistic-Models 时间: 2015-11-18 作…...

【每日运维】RockyLinux8.6升级OpenSSH9.4p1

为什么需要升级openssh呢&#xff0c;因为很多项目进行漏扫结果都会涉及到这个服务器核心组件&#xff0c;一想到以前升级openssh带来的各种依赖性问题就头疼&#xff0c;不管是什么发行版&#xff0c;升级这个东西真的很烦&#xff0c;这次发现可能还会有好一点的通用一点的升…...

libdrm全解析三十八 —— 源码全解析(35)

接前一篇文章&#xff1a;libdrm全解析三十七 —— 源码全解析&#xff08;34&#xff09; 本文参考以下博文&#xff1a; DRM 驱动程序开发&#xff08;VKMS&#xff09; 特此致谢&#xff01; 前一篇文章讲解完了drmModeSetCrtc(crtc_id, fb_id, connector_id, mode)&#…...

jar包和war包的区别

SpringBoot项目既可以打成war包发布&#xff0c;也可以找成jar包发布。 jar包 jar包&#xff1a;直接通过内置Tomcat运行&#xff0c;不需要额外安装Tomcat。如需修改内置Tomcat的配置&#xff0c;只需要在SpringBoot的配置文件中配置。内置Tomcat没有自己的日志输出&#xff0…...

CloudCompare 二次开发(10)——点云投影到平面

目录 一、概述二、代码集成三、结果展示一、概述 不依赖任何第三方点云相关库,使用CloudCompare编程实现点云投影到指定平面,具体计算原理见:PCL 点云投影到拟合平面 二、代码集成 1、mainwindow.h文件public中添加: void doActionProjectToPlane(); // 投影到平面2、…...

如何制作并运行 jar 程序

以下是用 Intellij 制作 jar 程序&#xff0c;并运行的方法。 【1】新建工程&#xff0c;保持默认选项&#xff0c;Next 【2】保持默认选项&#xff0c;Next 【3】给工程命名&#xff0c;设置保存位置&#xff0c;Finish 【4】新建工程结束&#xff0c;进入开发界面 【5】展开…...

Hadoop MapReduce 调优参数

文章目录 MapReduce 调优参数详解MapReduce 调优参数一键复制 前言&#xff1a; 下列参数基于 hadoop v3.1.3 版本&#xff0c;共三台服务器&#xff0c;配置都为 4 核&#xff0c;4G 内存。 MapReduce 调优参数详解 这个参数定义了在 Reduce 阶段同时进行的拷贝操作的数量&…...

springboot 与 Redis整合

SpringBoot 操作数据&#xff1a;Spring-data jpa jdbc mongodb redis! SpringData 也是和SpringBoot 齐名的项目&#xff01; 说明&#xff1a;在SpringBoot2.X 之后&#xff0c;原来使用的jedis被替换成了lettuce jedis&#xff1a; 采用的直连&#xff0c;多个线程操作的话&…...

如何高效地设计测试用例并评审

编写出好的测试用例是每一个测试工程师的职责&#xff0c;但在实际工作中大家写的测试用例往往需要不断地修改才能使用&#xff0c;这不仅浪费了时间&#xff0c;还容易让测试工程师产生自我否定的情绪&#xff0c;甚至在团队中产生各种矛盾。 那如何高效地设计测试用例呢&…...

基于python+Django知识图谱的医疗问答系统设计与实现

摘 要 从信息技术的发展至今&#xff0c;各色各样的技术能够满足各类人群的需求&#xff0c;能够让各种业务行业的痛点变成能够可以解决的方法&#xff0c;随着我们经济的不断提高&#xff0c;越来越多的人都该关注健康&#xff0c;那么健康饮食、健康医疗是我们生活中所追求的…...

cuda编程常见问题

cuda环境配置 参考链接 https://blog.csdn.net/weixin_43788327/article/details/125823507?spm1001.2014.3001.5506 疑难问题 在学习《GPU高性能编程CUDA实战》这本书时&#xff0c;发现书里面的代码很多变量都没定义过&#xff0c;经过搜索发现这都是书上自带的代码。 代…...

QTday3

作业1&#xff1a;通过对话框实现保存文件 void burger::on_saveBtn_clicked() {//调用函数&#xff0c;获取文件路径QString filename QFileDialog::getSaveFileName(this,"选择文件夹","./","Text File(*.txt)");if(filename.isNull()){QMes…...

docker镜像是如何导入的?

镜像导入是由image/tarexport/load.go#tarexporter.Load()完成的 以下代码参考github.com/docker/docker版本v0.0.0-20181129155816-baab736a3649 主要是注册镜像信息以及解包镜像tar流到新root 导出和保存的区别在于 导出(export): 仅导出文件结构保存(save): 保存镜像历史和元…...

四川大学874考研真题00-23

22, 2022年硕士学位研究生入学考试试题回忆版 数据结构 1&#xff0e;一个时间复杂度为n2 的算法运行&#xff0c;m1算n个问题用时1秒&#xff0c;m2处理器是m1效率的64倍&#xff0c;则m2每秒能计算&#xff08;&#xff09;个问题。 A. 64n B. 8n …...

openGauss学习笔记-58 openGauss 高级特性-资源池化

文章目录 openGauss学习笔记-58 openGauss 高级特性-资源池化58.1 特性简介58.2 架构介绍58.3 功能特点58.4 适用场景与限制58.5 手动安装示例58.6 OCK RDMA使用示例58.7 OCK SCRLock使用示例 openGauss学习笔记-58 openGauss 高级特性-资源池化 58.1 特性简介 资源池化特性主…...

centos升级cmake之相关问题解决

1. yum安装(仓库默认版本) # 查看可安装版本 yum --showduplicates list xxxx# 安装指定版本 yum install xxxx-1.23.1 2. 安装高版本cmake 2.1 一开始下载的是cmake-xxx.zip&#xff0c;在 ./bootstrap 一直会报错“missing terminating " character”&#xff0c;后来…...