Vue 3 文件预览落地:鉴权 Blob、Worker/WASM 与内网子路径部署

最近在一个 Vue 3 + Vite 的 OA 附件页里,我重新走了一遍 DOCX、XLSX、PPTX 和 PDF 的接入链路。开发环境只有一个目标:点附件后在当前页面预览;生产环境却多了三个硬约束:文件接口需要鉴权、站点部署在 /oa/ 子路径、运行时不能访问公网 CDN。

这类需求真正容易出错的地方并不是组件标签,而是文件怎么进入组件、Worker/WASM 从哪里加载,以及构建产物是否把整条预览链路一起交付。

本文给出一套可复现的 Vue 3 落地方法。示例以 2026-08-05 核验的 File Viewer v2.2.5 为基线;我在维护 File Viewer,但下面关于 URL、Blob、Range 和静态资源版本一致性的判断,同样适用于其他浏览器预览方案。

1. 先确定文件输入,不要先纠结组件参数

文件来源通常只有两类:可以直接访问的 URL,或者业务已经拿到的 File/Blob

公开 URL、同源 Cookie 接口、带短期签名的对象存储地址,可以直接交给预览组件。需要自定义鉴权头、解密或统一处理 401 的接口,应该由业务层完成下载,再把二进制包装为带正确扩展名的 File

先用 Full 包跑通完整链路:

bash 复制代码
pnpm add @file-viewer/vue3-full
pnpm add -D @file-viewer/vite-plugin
vue 复制代码
<script setup lang="ts">
import { ref } from 'vue'
import { FileViewer, type ViewerOptions } from '@file-viewer/vue3-full'

const selected = ref<File>()
const options: ViewerOptions = {
  theme: 'light',
  styleIsolation: 'auto',
  toolbar: { position: 'bottom-right' }
}

function openLocalFile(event: Event) {
  const input = event.target as HTMLInputElement
  selected.value = input.files?.[0]
}
</script>

<template>
  <input
    type="file"
    accept=".docx,.xlsx,.pptx,.pdf"
    @change="openLocalFile"
  >
  <FileViewer
    v-if="selected"
    :file="selected"
    :options="options"
    class="viewer"
  />
</template>

<style scoped>
.viewer {
  display: block;
  height: min(760px, 78vh);
}
</style>

Full 包已经带完整 preset,不要再次安装并注入同一套 renderer。只有文件类型明确、体积预算经过测量后,再考虑按能力拆包。

2. Worker、WASM 和字体必须跟着前端产物走

本地能打开、部署后白屏,最常见的原因不是 Vue 组件失效,而是只发布了 JavaScript 入口,没有发布解析器运行时。

Vite 配置需要复制同版本的 Worker、WASM、字体和 vendor 资源:

ts 复制代码
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileViewerRenderers } from '@file-viewer/vite-plugin'

export default defineConfig({
  base: '/oa/',
  plugins: [
    vue(),
    fileViewerRenderers({ copyAssets: true })
  ]
})

默认资源地址是部署基址下的 file-viewer/。站点基址是 /oa/ 时,应在生产 Network 面板里看到 /oa/file-viewer/...,而不是根路径 /file-viewer/... 或公共 CDN。

如果资产统一放在独立静态目录,可以显式设置:

ts 复制代码
import { setDefaultFullAssetBaseUrl } from '@file-viewer/vue3-full'

setDefaultFullAssetBaseUrl('/static/file-viewer/')

非 Vite 项目可以在构建阶段复制同版本资源:

bash 复制代码
npx --no-install file-viewer-copy-assets ./public/file-viewer

不要从另一版本的部署目录手工拷贝 WASM。组件、Worker 和 vendor 资源版本不一致时,业务层往往只能看到模糊的加载失败或空白页。

3. 鉴权接口先下载,再构造带文件名的 File

自定义 Header 不应该塞进预览器的每条内部请求。让业务请求层负责鉴权、重试和审计,预览器只接收已经授权的二进制,边界更清楚。

vue 复制代码
<script setup lang="ts">
import { ref } from 'vue'
import { FileViewer } from '@file-viewer/vue3-full'

const previewFile = ref<File>()
const errorMessage = ref('')

async function openAttachment(id: string, filename: string) {
  errorMessage.value = ''

  const response = await fetch(`/api/attachments/${id}`, {
    credentials: 'include',
    headers: { 'X-Preview-Request': '1' }
  })

  if (!response.ok) {
    errorMessage.value = `附件读取失败:${response.status}`
    return
  }

  const blob = await response.blob()
  previewFile.value = new File([blob], filename, {
    type: blob.type,
    lastModified: Date.now()
  })
}
</script>

<template>
  <button @click="openAttachment('42', '合同.docx')">
    预览合同
  </button>
  <p v-if="errorMessage">{{ errorMessage }}</p>
  <FileViewer v-if="previewFile" :file="previewFile" />
</template>

文件名不能随意丢掉。很多容器格式的 MIME 都不够稳定,正确扩展名仍是选择预览链路的重要依据。

这条方案也有明确代价:fetch + Blob 通常要等完整下载,并在浏览器内存中保留一份二进制。对超大 PDF,如果服务端支持同源 Range 请求,直接 URL 更适合渐进读取;需要强制自定义鉴权头时,则要在流式能力、内存和安全边界之间做取舍。

4. 四个扩展名背后是四条不同链路

DOCX、XLSX、PPTX、PDF 最终进入同一个组件,不代表底层是同一个解析器。

  • DOCX 需要处理 OpenXML 关系、样式、图片、字体和分批渲染。
  • XLSX 重点是工作表、合并单元格、样式和大表格 Worker。
  • PPTX 涉及幻灯片关系、主题、图片以及独立 Worker。
  • PDF 由 PDF.js 链路处理,服务端支持 Range 才有条件渐进读取。

发布日验证结果为:208 个已注册扩展名映射到 25 条预览链路。这里描述的是扩展名路由矩阵,不是 208 套独立渲染器,也不代表所有文件都能像素级还原。

本文的 PowerPoint 范围只包含 .pptx,不展开旧版 .ppt

5. 上线验收必须在生产基址和断网环境完成

我会固定检查下面五项,而不是只看开发服务器有没有报错:

  1. 使用真实 base 执行 build 和 preview,在 /oa/ 下打开页面。
  2. 用 DOCX、XLSX、PPTX、PDF 各一个脱敏样本检查 Worker、WASM、字体和 vendor 请求,确认没有 404。
  3. 同时验证 URL 与 File 两条输入,覆盖 401、CORS、Range、下载中断和快速切换附件。
  4. 验证内容而非首屏:Word 看页眉页脚和表格,Excel 看合并单元格和大表滚动,PPTX 看页序与图片,PDF 看目录、旋转页和打印。
  5. 断开公网再次测试,确认所有运行时资源都来自自己的网络。

最后还要记录浏览器版本、设备内存、文件大小、页数和峰值内存。没有这些条件,"大文件可用"没有可比较意义。

适用边界

浏览器本地预览适合文件已有业务权限、团队希望减少额外副本,并愿意把运行时资产和兼容性测试纳入前端交付的场景。

如果需要像素级统一输出、服务器全文检索、归档 PDF、病毒扫描或集中水印,服务端转码通常更合适。公开低敏文件且团队不想维护运行时,可以直接评估成熟 SaaS。需要编辑、批注协同或复杂公式重算时,应选 Office、CAD 或专业编辑器,而不是把只读预览器当成编辑器。

组件标签只是入口。文件输入、运行时版本、鉴权、内存和生产回归能一起闭环,才算真正交付了预览功能。

完整 API 与部署参数见 Vue 3 接入与内网部署文档

相关推荐
无人生还2 小时前
从 Vue3 到 React · 快速上手系列第 11 篇:状态管理
前端·vue.js·react.js
其美杰布-富贵-李2 小时前
Vue 3 模块导入教程:import、export 与文件组织
前端·javascript·vue.js
布兰妮甜3 小时前
Vue 路由进阶:路由守卫权限控制、动态路由、懒加载、路由缓存
vue.js·懒加载·动态路由·路由守卫权限控制·路由缓存
Cobyte3 小时前
Vite & Rollup 插件开发实践
前端·javascript·vue.js
ShuiShenHuoLe4 小时前
vue如何实现国际化,它来了!
前端·javascript·vue.js
其美杰布-富贵-李4 小时前
Vue Router 实战教程
前端·javascript·vue.js
名字还没想好☜4 小时前
React 用 IntersectionObserver 实现图片懒加载与无限滚动:封装一个 useInView Hook
前端·javascript·vue.js·react.js·react
天天码行空21 小时前
vkeyboardhand:零依赖虚拟键盘指法组件
前端·javascript·vue.js
我要两颗404西柚21 小时前
Stage four:VUE项目上线
前端·javascript·vue.js