一行代码优化 pdfjs 加载大文件的pdf 速度

目录

    • 介绍
    • 问题
    • 分析
    • 解决
    • 结束

介绍

先简单介绍下pdfjs 怎么 去加载pdf文件

import * as PDFJS from 'pdfjs-dist/legacy/build/pdf'
PDFJS.GlobalWorkerOptions.workerSrc = require('pdfjs-dist/legacy/build/pdf.worker.entry.js')// blobUrl container指 dom 承载pdf 的容器
export const loadAndDisplayPdfByBlobUrl = (blobUrl, container) => {// 加载PDF文件PDFJS.getDocument(blobUrl).promise.then(async (pdf) => {const totalPages = pdf.numPages// 循环绘制每个页面 for (let pageNum = 1; pageNum <= totalPages; pageNum++) {let page = await pdf.getPage(pageNum)const viewport = page.getViewport({ scale: 1 })const canvas = createCanvasDom(viewport.width, viewport.height)container.appendChild(canvas)const context = canvas.getContext('2d')// 将每一页pdf内容 渲染页面到canvasawait page.render({canvasContext: context,viewport: viewport,})}})
}const createCanvasDom = (width, height) => {const canvas = document.createElement('canvas')// 设置canvas的宽度和高度canvas.width = widthcanvas.height = heightreturn canvas
}

简单 页面使用 (vue2 举例)

<template><div>// 为了测试 自己上传pdf <Uploader :after-read="afterRead" accept="*"></Uploader><div id="pdf"></div></div>
</template><script>
import { loadAndDisplayPdfByBlobUrl } from './handel'
import { Uploader } from 'vant'export default {components: {Uploader,},data() {return {fileUrl: '',}},mounted() {loadAndDisplayPdfByBlobUrl()},methods: {afterRead(file) {// 将 file 对象 转化成 blobUrl this.fileUrl = URL.createObjectURL(file.file)loadAndDisplayPdfByBlobUrl(this.fileUrl, document.querySelector('#pdf'))},},
}
</script>

问题

上面的写法 处理体积小的 pdf文件 不会出现啥问题

当文件过大的时候 渲染量过大 很长一段时间界面会出现白屏 。用户体验不好

如下图:

在这里插入图片描述
这个js 忍者秘籍pdf 400多页 ,从上传后到 页面出现结果 消耗了大约 11s

全网最多的解决方案就是 传入的fileUrl 支持 分片下载,在开启 disableRange

export function getDocument(src: GetDocumentParameters): PDFDocumentLoadingTask;export type GetDocumentParameters = string | URL | TypedArray | ArrayBuffer | PDFDataRangeTransport | DocumentInitParameters;// 在 DocumentInitParameters 类型中有个 属性
/*** - Disable range request loading of PDF* files. When enabled, and if the server supports partial content requests,* then the PDF will be fetched in chunks. The default value is `false`.*/disableRange?: boolean | undefined;
PDFJS.getDocument(url, {disableRange: true
}).then(function(pdfDocument) {// 处理 PDF 文档
});

还需要后端改动 为了让后端小伙伴安心的摸鱼,还是前端自己来吧

分析

在这里插入图片描述
会发现 微任务 执行耗时太久 , 页面 没有发生render

这涉及到 一个 队列优先级的问题,粗略说下

在这里插入图片描述
优先级一般都是从上到下

我们可以得出结论 :
微任务执行 阻塞了 渲染(每一次微任务执行后 会创建下一页渲染的微任务 主线程一直会执行微任务队列里面的任务 会被一直占用)

解决

我们可以考虑在每一页 渲染的时候 (微任务)中间插入一个 比渲染队列低的任务 空出时间给 主线程去执行 渲染队列的 任务

可以 创建一个 延时队列的任务 主线程会执行渲染队列任务 后在执行延时队列任务
等待延时,后再创建 渲染pdf下一页的微任务。反复如此执行

最终代码如下

import * as PDFJS from 'pdfjs-dist/legacy/build/pdf'
PDFJS.GlobalWorkerOptions.workerSrc = require('pdfjs-dist/legacy/build/pdf.worker.entry.js')export const loadAndDisplayPdfByBlobUrl = (blobUrl, container) => {// 加载PDF文件PDFJS.getDocument(blobUrl).promise.then(async (pdf) => {const totalPages = pdf.numPages// 循环绘制每个页面for (let pageNum = 1; pageNum <= totalPages; pageNum++) {let page = await pdf.getPage(pageNum)const viewport = page.getViewport({ scale: 1 })const canvas = createCanvasDom(viewport.width, viewport.height)container.appendChild(canvas)const context = canvas.getContext('2d')// 渲染页面到canvasawait page.render({canvasContext: context,viewport: viewport,})// 下一页 渲染 前创建 延时队列任务 延时时间可以自己调整 这里为了测试效果写了100ms 一般 100ms 效果就很OK了await sleep(100)}})
}const createCanvasDom = (width, height) => {const canvas = document.createElement('canvas')// 设置canvas的宽度和高度canvas.width = widthcanvas.height = heightreturn canvas
}const sleep = (time) => {return new Promise((resolve) => {setTimeout(() => {resolve(time)}, time)})
}

优化后效果如下:

在这里插入图片描述
2s 左右 pdf 第一页就出来了

后面基本 间隔 等待时间 后渲染下一页 表现如下:
在这里插入图片描述

结束

优化后 总渲染时间会变长 ,交互效果会更好

后面抽时间总结一篇 浏览器事件循环 的文章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/103679.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

stm32学习笔记:中断的应用:对射式红外传感器计次旋转编码器计次

相关API介绍 EXT配置API(stm32f10x exti.h&#xff09; NVIC 配置API (misc.h) 初始化的中断的步骤 第一步&#xff1a;配置RCC时钟&#xff0c;把涉及外设的时钟都打开 第二步&#xff1a;配置GPIO&#xff0c;设置为输入模式 第三步&#xff1a;配置AFIO&#xff0…

基于Java使用SpringBoot+Vue框架实现的前后端分离的美食分享平台

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 在当今社会&#xff0…

03 | Defining Query Methods 的命名语法与参数

Spring Data JPA 的最大特色是利用方法名定义查询方法&#xff08;Defining Query Methods&#xff09;来做 CRUD 操作&#xff0c;这一课时我将围绕这个内容来详细讲解。 在工作中&#xff0c;你是否经常为方法名的语义、命名规范而发愁&#xff1f;是否要为不同的查询条件写…

软件测试学习(五)

报告发现的问题 设法修复软件缺陷 ●没有足够的时间。在任何一个项目中&#xff0c;通常是软件功能太多&#xff0c;而代码编写人员和软件测试人员太少&#xff0c;而且进度中没有留出足够的空间来完成项目。假如你正在制作税务处理程序&#xff0c;4月15日 (赶在应付税务检查…

串的基本操作(数据结构)

串的基本操作 #include <stdlib.h> #include <iostream> #include <stdio.h> #define MaxSize 255typedef struct{char ch[MaxSize];int length; }SString;//初始化 SString InitStr(SString &S){S.length0;return S; } //为了方便计算&#xff0c;串的…

LeetCode【74】搜索二维矩阵

题目&#xff1a; 代码&#xff1a; public static boolean searchMatrix(int[][] matrix, int target) {int rows matrix.length;int columns matrix[0].length;// 先找到行&#xff0c;行为当前行第一列<target&#xff0c;当前行1行&#xff0c;第一列>targetfor…

【TA 工具积累】参考图展示 PureRef | 截图 Snipaste

贴两个平常看图和截图比较方便的工具&#xff1a; PureRef 官网指路&#xff1a;PureRef 油管简单的使用教程视频&#xff1a;Free Download | PureRef 知乎上大佬总结的快捷键&#xff1a; PureRef 快捷键 提炼总结 - 知乎 (zhihu.com) b站大佬总结的快捷键&#xff1a;…

一文告知HTTP GET是否可以有请求体

HTTP GET是否可以有请求体 先说结论&#xff1a; HTTP协议没有规定GET请求不能携带请求体&#xff0c;但是部分浏览器会不支持&#xff0c;因此不建议GET请求携带请求体。 HTTP 协议没有为 GET 请求的 body 赋予语义&#xff0c;也就是即不要求也不禁止 GET 请求带 body。大多数…

Idea集成Docker

1、前言 上一节中&#xff0c;我们介绍了Dockerfile的方式构建自己的镜像。但是在实际开发过程中&#xff0c;一般都会和开发工具直接集成&#xff0c;如Idea。今天就介绍下idea和Docker如何集成。 2、开启docker远程 要集成之前&#xff0c;需要我们本机能够访问docker服务…

基于LoRa的远程气象站:实现远程气象监测与数据传输

随着物联网技术的不断发展&#xff0c;基于无线通信的远程气象监测系统得以广泛应用。本文将介绍一种基于LoRa技术的远程气象站&#xff0c;通过LoRa模块实现气象数据的远程采集和传输&#xff0c;为气象监测提供了一种高效、低功耗的解决方案。 LoRa技术概述 LoRa&#xff08…

ssm+vue的课程网络学习平台管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的课程网络学习平台管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体…

《从程序员到架构师》:从现在开始培养架构思维,一点都不晚

《从程序员到架构师》&#xff1a;从现在开始培养架构思维&#xff0c;一点都不晚 尽管大家都明白软件架构非常重要&#xff0c;但是能够真正理解并应用软件架构的核心思维去解决实战的商业项目&#xff0c;确实大多数程序员所欠缺的。本文将从一个全新的视角&#xff0c;重新带…

【算法练习Day19】二叉搜索树的最近公共祖先二叉搜索树中的插入操作删除二叉搜索树中的节点

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 二叉搜索树的最近公共祖先叉…

Avalonia常用小控件Svg

1.项目下载地址&#xff1a;https://gitee.com/confusedkitten/avalonia-demo 2.UI库Semi.Avalonia&#xff0c;项目地址 https://github.com/irihitech/Semi.Avalonia 3.SVG库&#xff0c;Avalonia.Svg.Skia&#xff0c;项目地址 https://github.com/wieslawsoltes/Svg.Ski…

【数据库——MySQL(实战项目1)】(4)图书借阅系统——触发器

目录 1. 简述2. 功能代码2.1 创建两个触发器&#xff0c;分别在借出或归还图书时&#xff0c;修改借阅人表中的已借数目(附加&#xff1a;借阅人表的总借书数、图书表的借阅次数以及更新图书表的图书状态为(已借出/在架上))字段&#xff1b;2.2 创建触发器&#xff0c;当借阅者…

redis 哨兵 sentinel(一)配置

sentinel巡查监控后台master主机是否故障&#xff0c;如果故障根据投票数自动将某一个从库转换为新主库&#xff0c;继续对外服务 sentinel 哨兵的功能 监控 监控主从redis库运行是否正常消息通知 哨兵可以将故障转移的结果发送给客户端故障转移 如果master异常&#xff0c;则…

IOS17 轻松签全能签还能不能用?多开能否使用?升级后微信底栏消失怎么办?BY:后厂村路灯

从iphone15还没出就有小伙伴们追着问&#xff0c; 到现在也有人一直再问iOS17能不能用&#xff0c;看来换手机的人很多呀。 这里统一回答一下&#xff1a;“iOS17苹果签名可以用&#xff0c;多开也可以用”但是还是有些地方注意。 如果你是16系统直接升级刀17就可以&#xff…

使用 Splashtop 驾驭未来媒体和娱乐

在当今时代&#xff0c;数字转型不再是可选项&#xff0c;而是必选项。如今&#xff0c;媒体与娱乐业处于关键时刻&#xff0c;正在错综复杂的创意、技术和远程协作迷宫之中摸索前进。过去几年发生的全球事件影响了我们的日常生活&#xff0c;不可逆转地改变了行业的运作方式&a…

给你一个项目,你将如何开展性能测试工作?

一、性能三连问 1、何时进行性能测试&#xff1f; 性能测试的工作是基于系统功能已经完备或者已经趋于完备之上的&#xff0c;在功能还不够完备的情况下没有多大的意义。因为后期功能完善上会对系统的性能有影响&#xff0c;过早进入性能测试会出现测试结果不准确、浪费测试资…

【C++STL基础入门】list基本使用

文章目录 前言一、list简介1.1 list是什么1.2 list的头文件 二、list2.1 定义对象2.2 list构造函数2.3 list的属性函数 总结 前言 STL&#xff08;Standard Template Library&#xff09;是C标准库的一个重要组成部分&#xff0c;提供了一套丰富的数据结构和算法&#xff0c;可…