ARTICLE · 1331793
PDF.js 淘汰!Vue3 工具爆火!统一 pdf excel docx!
后台管理系统做久了,就会发现一个很烦人的需求:文件预览。
PDF 要接 PDF.js,Excel 要处理表格,Word 又得找一套文档预览方案。项目里装了一堆依赖,页面还没写多少,光是文件预览就折腾出好几套实现。
更麻烦的是,业务页面根本不关心文件内部怎么解析。用户点开一个附件,只想看内容,至于它是 PDF、Excel 还是 DOCX,最好由组件自己处理。
最近 Vue3 生态里有一类统一文件预览工具,思路就是把这些格式收进同一个组件。这里要注意,并不是 PDF.js 被彻底淘汰了,而是没必要再让业务页面直接维护每一种文件格式的预览逻辑。 PDF.js 仍然可以作为底层 PDF 渲染引擎。
假设后台有个合同管理页面,附件可能是 PDF 合同、Excel 报价单,也可能是 Word 版的说明文档。
以前的做法通常是:
PDF:单独写 PDF 预览组件。 Excel:读取工作簿,再自己渲染表格。 DOCX:引入文档解析库,处理段落、表格和样式。
这几套代码分散在不同页面,后面要加一个文件格式,又得改一轮。
如果选用支持 Vue3 的统一预览组件,业务层只需要负责两件事:拿到文件地址,把文件交给预览组件。
比如可以把调用入口统一成这样:
<script setup>
import { ref } from 'vue'
import FilePreview from '@/components/FilePreview.vue'
const currentFile = ref({
name: '报价单.xlsx',
url: '/api/files/quotation.xlsx'
})
function openAttachment(file) {
currentFile.value = file
}
</script>
<template>
<FilePreview
v-if="currentFile"
:src="currentFile.url"
:file-name="currentFile.name"
/>
</template>
这里的 FilePreview 是业务层的统一组件接口示例,并非某个具体开源项目的原生 API。实际使用时,需要按所选工具的组件名称和参数调整。
这样改完,合同列表、订单详情、审批记录都可以复用同一个入口。业务页面不用再判断 PDF 怎么加载、Excel 怎么解析,后续维护也清爽不少。
不过,别急着把所有文件都往组件里塞。统一的是调用方式,不代表所有文件的解析方式都一样。
不同格式的文件,处理方式差别很大。
PDF 关注分页、缩放、文本选择和页面渲染;Excel 需要解析工作表、单元格、合并区域;DOCX 则涉及段落、表格、图片和样式。
统一预览工具通常是在外层做格式识别,再调用相应的解析或渲染能力。
举个例子,下面这段代码就是一个简单的格式分发逻辑。不是完整的预览引擎,但足以说明组件内部应该怎么组织。
const previewHandlers = {
pdf: renderPdf,
xlsx: renderExcel,
xls: renderExcel,
docx: renderDocx
}
function getFileType(fileName) {
const index = fileName.lastIndexOf('.')
return index < 0
? ''
: fileName.slice(index + 1).toLowerCase()
}
async function previewFile(file) {
const type = getFileType(file.name)
const handler = previewHandlers[type]
if (!handler) {
throw new Error(`暂不支持预览该文件:${type || '未知格式'}`)
}
return handler(file.url)
}
其中的 renderPdf、renderExcel 和 renderDocx 需要由具体的解析库实现。
这种写法有个好处:以后增加 CSV、PPTX 等格式,不用在每个业务页面里堆一串 if...else,只需要扩展对应的处理器。
当然,支持多少种格式、是否支持分页和复杂样式,还是要看具体工具,不能只看宣传页上的格式列表。
本地拿几个小文件测试,通常看不出什么问题。上线后,文件预览可能会遇到跨域、鉴权、内存占用和大文件加载等麻烦。
尤其是企业内部系统,文件地址经常不是一个可以直接访问的静态 URL,而是需要携带登录凭证的接口。
比如下面这种情况:
async function loadPreviewFile(url) {
const response = await fetch(url, {
credentials: 'include'
})
if (!response.ok) {
throw new Error(`文件加载失败:HTTP ${response.status}`)
}
const blob = await response.blob()
return URL.createObjectURL(blob)
}
这段代码适合需要通过 Cookie 鉴权获取文件的场景,但也有代价:blob() 通常要先把整个响应读入内存。碰上几百 MB 的 PDF,就不能想当然地这么处理了。
大文件预览更适合考虑 HTTP Range 分段请求、按页加载等能力。使用哪种方式,要看底层预览库是否支持,以及服务端有没有正确处理 Range 请求。
还有一个细节:通过 URL.createObjectURL() 创建的临时地址,使用完记得调用 URL.revokeObjectURL() 释放。反复打开、关闭预览页面,却一直不释放,内存占用可能会慢慢涨起来。
至于 Excel 和 Word,也别把“能够打开”当成“完全还原”。复杂公式、特殊字体、分页布局以及嵌入对象,都可能与原始 Office 软件中的显示效果不同。
如果业务要求严格还原排版,服务端转换成 PDF 往往更值得考虑。
最后再说一句,选择 Vue3 文件预览工具时,我更在意三个地方:格式支持是否真实可用、项目是否持续维护、遇到大文件时会不会拖垮浏览器。
如果只是展示 PDF,继续使用 PDF.js 完全没问题;如果一个后台同时要预览 PDF、Excel、DOCX,统一预览组件确实能减少不少重复代码。
工具可以换,底层原理还是得搞清楚。否则,开发时省下的几行代码,最后可能全变成线上排查的时间。















































































