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

这篇具有很好参考价值的文章主要介绍了记录--前端使用a链接下载内容增加loading效果。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

记录--前端使用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' })
  // 字符串转ArrayBuffer
  function 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) & 0xff
    return 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 = url
  aLink.download = saveName + '.xlsx' || '1.xlsx' // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效
  var event
  if (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 = true
  const 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.js

onmessage = 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效果文章来源地址https://www.toymoban.com/news/detail-685212.html

到了这里,关于记录--前端使用a链接下载内容增加loading效果的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微信小程序显示loading效果,微信小程序节流效果,微信小程序生命周期,微信小程序组件的创建与使用

            文档地址:wx.showLoading(Object object) | 微信开放文档         请求前 显示loading 效果 ,请求结束后隐藏loading         下拉触底需要时间比较长,但是可能会一直进行请求,我们可以 设置一个 加载状态 ,用来控制什么时候 才可以加载         当我们loading 效果为tr

    2024年02月10日
    浏览(34)
  • 前端用原生js编辑文件内容→创建生成文件(格式可以自定义)→下载文件

    会自动创建一个html文件    前端实现下载文件的两种方法downloadFile_文件下载downloadfile_你挚爱的强哥的博客-CSDN博客 实现效果如上图。_文件下载downloadfile https://blog.csdn.net/qq_37860634/article/details/131022008 通过整合以上两个方法,合并为一个方法: 特性: 实现自主编写文件生成并

    2024年02月12日
    浏览(39)
  • 【记录一次前端图片下载问题】解决跨域+直接下载

    近日有个需求需要下载协议照片,使用的是阿里云的oss,由于无法协调后端那边配置跨域响应头,找了很多方案都不理想,终于在摸索下可以实现完美下载 常见方案有两个问题 1.图片格式(png,jpg等)不会触发下载,直接打开预览 2.跨域问题 先捋一下常见方案 方法一、a标签下载

    2024年04月23日
    浏览(26)
  • vue中使用echarts实现省市地图绘制,根据数据在地图上显示柱状图信息,增加涟漪特效动画效果

    一、实现效果 使用echarts实现省市地图绘制,你也可以绘制全国地图。 根据数据在地图显示柱状图,根据经纬度实现定位。 根据数据显示数据,涟漪动态效果。 当然你也可以根据你自己的需求,增删效果哦。 二、实现方法 1、安装echarts插件 2、获取省市json数据 https://datav.

    2024年02月03日
    浏览(34)
  • 记录--盘点前端实现文件下载的几种方式

    前端涉及到的文件下载还是很多应用场景的,那么前端文件下载有多少种方式呢?每种方式有什么优缺点呢?下面就来一一介绍。 通过 a 标签的 download 属性来实现文件下载,这种方式是最简单的,也是我们比较常用的方式,先来看示例代码: 就上面的这个示例,我们点击下

    2024年02月13日
    浏览(41)
  • Android——使用ScrollView实现滚动效果,当内容超出屏幕范围时自动滑动显示

    Android——使用ScrollView实现滚动效果,当内容超出屏幕范围时自动滑动显示 ScrollView是Android中常用的布局容器,用于在屏幕空间有限的情况下实现内容的滑动显示。当内容超出屏幕范围时,用户可以通过滑动屏幕来查看更多内容,提供了更好的用户体验。 在Android中,使用Sc

    2024年01月16日
    浏览(36)
  • 【UE】富文本块(RichTextBlock) 增加超链接支持

    RichTextBlock使用方式参考官方文档:RichTextBlockUE 文档中描述了不同样式文字以及图片的使用,但没有具体说明超链接的实现,但库中有hyperlink结构。 因此照猫画虎实现,仿照RichImgDecorator实现。 首先在build.cs 里增加Slate引用 编写SRichTextHyperlink_Ex.h文件 基于SRichTextHyperlink的拓展

    2024年02月09日
    浏览(24)
  • css增加高斯模糊的效果

    backdrop-filter 当你创造一个元素加上这个属性后,会使得这个元素后面的区域添加效果(如模糊或颜色偏移)

    2024年02月11日
    浏览(38)
  • BurpSuite超详细安装教程-功能概述-配置-使用教程---(附下载链接)

    BurpSuite是渗透测试、漏洞挖掘以及Web应用程序测试的最佳工具之一,是一款用于攻击web 应用程序的集成攻击测试平台,可以进行抓包、重放、爆破,包含许多工具,能处理对应的HTTP消息、持久性、认证、代理、日志、警报。 链接: 百度网盘链接 提取码:ranz 下载后解压 如

    2024年02月03日
    浏览(32)
  • 在WPF窗口中增加水印效果

    ** ** 以Canvas作为水印显示载体,在Canvas中创建若干个TextBlock控件用来显示水印文案,如下图所示 然后以每一个TextBlock的左上角为中心旋转-30°,最终效果会是如图红线所示: 为了达到第一行旋转后刚好与窗口上边沿齐平,需要计算第一行其实位置的Top坐标,由于旋转角度为

    2024年02月04日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包