记录一下vue3+vite使用pdf.js的经验和遇到的坑......
1、安装pdf.js
npm install pdfjs-dist
2、使用案例如下:
(1) 创建pdfService.js
import { getDocument } from 'pdfjs-dist';
const pdfService = {
async loadPdf(url) {
return await getDocument(url).promise;
},
async getPage(pdf, pageNum) {
return await pdf.getPage(pageNum);
},
};
export default pdfService;
(2) 组件页面代码:
<template>
<div id="document">
<div class="documentFlex document-left">
<h3>浙江自然:海外基地增长可观</h3>
<div class="date">2024-11-12 19:11</div>
<div class="canvas">
<canvas
ref="canvas"
:id="`pdfCanvas${page}`"
v-for="page in totalPages"
:key="page"
></canvas>
</div>
</div>
</div>
</template>
<script lang="ts">
import {
defineComponent,
nextTick,
onMounted,
reactive,
ref,
toRefs,
} from "vue";
import pdfService from "./pdfService";
export default defineComponent({
setup(props) {
const canvas = ref(null);
const state = reactive({
activeName: "1",
pdf: <any>null,
currentPage: 1,
totalPages: 0,
});
onMounted(async () => {
//1.如果后台返回具体的pdf地址http则使用一下方法
state.pdf = await pdfService.loadPdf(
"https://path/file"
); //从后台拿到pdf地址
state.totalPages = state.pdf.numPages;
nextTick(() => {
renderPage(state.currentPage); // 表示渲染第 1 页
});
//2.如果后台返回的是pdf文件乱码则需要利用blob转化(res是从后台获取的乱码)
// let blob = new Blob([res], {
// type: "application/pdf",
// });
// const pdfUrl = URL.createObjectURL(blob);
// // 加载 PDF 文件
// pdfService.loadPdf(pdfUrl).then((res) => {
// state.pdf = res;
// state.totalPages = res.numPages;
// nextTick(() => {
// renderPage(state.currentPage); // 表示渲染第 1 页
// });
// });
});
const renderPage = async (pageNum) => {
const page = await pdfService.getPage(state.pdf, pageNum);
let canvas: any = document.getElementById(`pdfCanvas${pageNum}`);
let ctx = canvas.getContext("2d");
let dpr = window.devicePixelRatio || 1;
let bsr =
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||
1;
let ratio = dpr / bsr;
let viewport = page.getViewport({ scale: 1 });
canvas.width = viewport.width * ratio;
canvas.height = viewport.height * ratio;
// canvas.style.width = "100%";
// canvas.style.height = "100%";
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
// 将 PDF 页面渲染到 canvas 上下文中
let renderContext = {
canvasContext: ctx,
viewport: viewport,
};
page.render(renderContext);
if (state.totalPages > pageNum) {
renderPage(pageNum + 1);
}
};
return {
canvas,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
#document {
padding: 20px;
display: flex;
width: 100%;
background: #fff;
justify-content: space-between;
.document-left {
text-align: center;
display: flex;
flex-direction: column;
.date {
color: rgba(51, 51, 51, 0.65);
font-size: 14px;
}
.canvas {
overflow: auto;
margin-top: 20px;
}
}
}
</style>
3.踩得坑:
(1)
莫名其妙报了个 getPage的错误,上网找了一下说是和vue3的proxy有关,pdfjs-dist拦截校验的时候,应该是需要一个原始对象,但通过vue3获取到的是一个proxy响应式的对象,这里使用toRaw()将proxy转化为原始对象就可以了。修改pdfService.js:
import { getDocument } from 'pdfjs-dist';
import * as pdfjsLib from "pdfjs-dist/build/pdf.worker.min.mjs";
import { toRaw } from 'vue';//使用toRaw
const pdfService = {
async loadPdf(url) {
return await getDocument(url).promise;
},
async getPage(pdf, pageNum) {
return await toRaw(pdf).getPage(pageNum);//使用toRaw
},
};
export default pdfService;
(2)
处理好上面之后 对于最新的浏览器版本是没有问题的,但是后面经过测试发现低版本浏览器比如谷歌可能会出现下面报错:
在Vue 3中,Promise.withResolvers
方法不存在,通常是因为在代码中使用的环境不支持Promise.withResolvers ,或者使用的 pdfjs 库和浏览器环境不兼容。 Promise.withResolvers 是 ES2023 引入的一个新特性,允许你同时创建一个 Promise 对象和它的 resolve 和 reject 函数。如果浏览器版本不支持这个特性,就会出现这个错误。
解决方案:
(1)安装core-js:
npm install core-js
如果有用到async/await,需要安装regenerator-runtime:
npm install --save regenerator-runtime
Vue 3 是基于 ES6+ 特性的,因此如果你想在你的项目中引入 core-js 的 polyfill,你需要配置babel 或者通过 Vite 配置来确保它被正确引入。
(2)修改 vite.config.js 配置:
npm install @vitejs/plugin-vue
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
define: {
'process.env': {},
},
build: {
target: 'esnext', // 根据需要修改目标版本
},
});
(3)在入口文件main.js 或 main.ts 中引入 core-js 和regenerator-runtime:
import 'core-js/stable'; // 引入所有稳定版本的 polyfills
import 'regenerator-runtime/runtime'; // 如果你使用了 async/await,可能需要这个
这样就兼容了低版本的浏览器。