当前位置: 首页 > 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】展开…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...