首先聲明一下,pdfjs-dist要是版本安裝不對,會出現各種各樣的bug!!!
目前我項目中pdfjs-dist的版本和nuxt的版本:
"nuxt": "^2.15.8",
"pdfjs-dist": "2.3.200",
我項目中版本錯誤遇到的錯誤:PDF.js報錯workerSrc修復
一、項目背景
因為公司的項目中要做合同簽訂的功能,需要涉及到用戶預覽合同的功能,因此我的痛苦就到來了o(╥﹏╥)o
這個功能最初討論之后是由后端返回pdf的鏈接,前端同學實現pdf在線預覽的效果,效果圖如下:

二、功能實現
最初我在網上經過查找決定使用vue-pdf的插件來實現pdf預覽的功能,在本地開發中實現了功能,發布到測試環境也運行正常。
最最讓人擔心的事情還是發生了,發布到正式環境之后整個項目都報錯了。我真是吐血了。。。。
Uncaught DOMException: Failed to construct 'Worker': Script at 'http://a.com/ef3086d432cfbec65966.worker.js' cannot be accessed from origin 'http:/b.com'.
經過一番查找終於找到了具體原因,是因為vue-pdf里面為了加快pdf的渲染使用了Worker ,因為我們項目在正式環境當中靜態文件之類的是放在七牛上面的,然而worker 是不能跨域的,將頁面和靜態資源分開的場景,就會出現跨域問題。--解決 vue-pdf 打包后跨域報錯、Web Workers 資源跨域問題
三、解決方案
node_modules/vue-pdf/src/vuePdfNoSss.vue 中,有一個 Worker 支持的判斷,一般的vue項目當中可以直接注釋掉重新打包就可以了。
// 注釋if內的兩句代碼或者注釋整個if語句 if ( typeof window !== 'undefined' && 'Worker' in window && navigator.appVersion.indexOf('MSIE 10') === -1 ) { // var PdfjsWorker = require('worker-loader!pdfjs-dist/build/pdf.worker.js'); // PDFJS.GlobalWorkerOptions.workerPort = new PdfjsWorker(); }
注釋掉上面的代碼,就不走 web worker 了,頁面能夠正常運行了;相對的,頁面加載時間可能會變長,性能可能會降低;
注意:因為我的項目使用了自動化部署,每次部署的時候都會重新npm i拉取新的包,這樣的話我在本地修改vue-pdf的文件就不行了
基於以上原因,我只能更換插件重新尋找合適的插件。經過一番查找更換為pdfjs-dist插件,具體實現:
<template>
<div class="pdf-preview-container">
<div
v-for="page in docPages"
:key="page"
ref="container"
class="page-container"
:style="{
height: `${pageHeight}px`,
}"
>
<canvas v-if="renderList.includes(page)"> </canvas>
</div>
</div>
</template>
<script>
export default {
props: {
url: {
type: String,
required: true,
},
renderPages: {
type: Number,
default: 5,
},
customScroll: {
type: Boolean,
default: false,
},
offsetHeight: {
type: Number,
default: 0,
},
},
data() {
return {
doc: null,
docPages: 0,
currentPage: 0,
pageHeight: 0,
renderList: [],
}
},
watch: {
url: {
immediate: true,
handler() {
this.getPDFFile()
},
},
},
mounted() {
if (!this.customScroll) {
document.addEventListener('scroll', this.scroll)
}
},
beforeDestroy() {
document.removeEventListener('scroll', this.scroll)
},
methods: {
getPDFFile() {
if (!this.url) return
this.currentPage = 0
const pdfJS = require('pdfjs-dist/build/pdf')
const pdfjsWorker = require('pdfjs-dist/build/pdf.worker.entry')
pdfJS.GlobalWorkerOptions.workerSrc = pdfjsWorker
pdfJS.getDocument(this.url).then(pdf => {
// console.log('pdf: ', pdf)
this.doc = pdf
this.docPages = pdf._pdfInfo.numPages
this.$nextTick(() => {
this.docPages && this.scrollToPage(1)
})
})
},
scrollToPage(pageNo) {
if (this.currentPage === pageNo) return
this.currentPage = pageNo
let list = []
for (
let page = pageNo - this.renderPages;
page <= pageNo + this.renderPages;
page++
) {
list.push(page)
}
list = list.filter(page => page <= this.docPages && page >= 1)
this.$nextTick(() => {
this.renderList = list
this.renderList.forEach(page => {
this.renderPage(page)
})
})
},
// 渲染page
renderPage(pageNo) {
this.doc.getPage(pageNo).then(page => {
// console.log('page: ', page)
const container = this.$refs.container[pageNo - 1]
if (!container) return
const canvas = container.querySelector('canvas')
if (!canvas || canvas.__rendered) return
const ctx = canvas.getContext('2d')
const dpr = window.devicePixelRatio || 1
const bsr =
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||
1
const ratio = dpr / bsr
const rect = container.getBoundingClientRect()
const viewport = page.getViewport(1)
const width = rect.width
const height = (width / viewport.width) * viewport.height
canvas.style.width = `${width}px`
canvas.style.height = `${height}px`
this.pageHeight = height
canvas.height = height * ratio
canvas.width = width * ratio
ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
page.render({
canvasContext: ctx,
viewport: page.getViewport(width / viewport.width),
})
canvas.__rendered = true
})
},
scroll() {
this.checkRender(document.documentElement)
},
checkRender(el) {
if (!this.pageHeight) return
let scrollTop = el.scrollTop
if (el === document.documentElement) {
scrollTop =
el.scrollTop || window.pageYOffset || document.body.scrollTop
}
let page = Math.floor((scrollTop - this.offsetHeight) / this.pageHeight)
page = Math.max(page, 1)
page = Math.min(page, this.docPages)
this.scrollToPage(page)
},
},
}
</script>
頁面上調用:
<template>
<div id="app">
<pdf-preview :url="url"></pdf-preview>
</div>
</template>
<script>
import pdfPreview from "./components/PdfPreview.vue";
export default {
name: "app",
components: {
pdfPreview,
},
data() {
return {
url: "/static/pdf.pdf",
};
},
};
</script>
參考鏈接:https://github.com/Lushenggang/pdf-preview
經過一番折騰終於解決了這個問題,真是吐血了。

