☰
Vue3+Vite环境下文档在线预览全攻略:PDF/DOCX/PPTX内外网与移动端适配
2026/10/1 20:08:50 网站建设 项目流程

最近被安排了一个听上去很标准的预览需求:在 vue3 + vite 项目里在线预览 docx、pdf、pptx,文件可能来自外网也可能来自纯内网环境,而且用户会在手机上打开。等真正动手做才发现,这个需求就像一棵挂满装饰品的圣诞树,每个灯泡都有踩碎的可能。PDF 有 pdf.js 这种成熟方案,docx 也有相对靠谱的开源渲染库,唯独 pptx,前端圈几乎没有一个能打的纯本地方案;再加上内外网这个变量,直接把很多"网上抄来的代码"判了死刑。

这篇文章把我从选型到落地、从桌面端到移动端的完整过程写出来,包括三类文件各自的最优预览链路、内外网差异下的工程化处理、以及移动端的手势和适配细节。如果你也要接一个这样的文档预览模块,这篇文章能帮你少走至少一周的弯路。

1. 三类文件预览难度完全不同,先认清对手再选型

1.1 PDF 是渲染型文档,pdf.js 几乎是不二之选

PDF 的页面是固定版式:每个字符、图片、线条在输出时位置已经钉死,所以浏览器才可能直接显示。但不要图省事用 iframe 或 embed 直接塞一个 PDF URL,桌面 Chrome 里看着还行,换到 Safari、iOS WebView、安卓内置浏览器,表现差别非常大,有的直接白屏,有的只显示第一页,而且手机上没有翻页工具栏,用户只能干瞪眼。

pdf.js 是 Mozilla 官方维护的渲染库,能在浏览器 Canvas 里把每一页画出来,翻页、缩放、旋转都自己控制,彻底摆脱浏览器内核差异。另一个关键点是内网场景:pdf.js 可以完整打包到本地,不依赖外网 CDN,这对离线环境的稳定性是决定性的。所以 PDF 这条链路从第一天起就没犹豫过。

1.2 DOCX 本质是个 zip 包,得解析成可渲染内容

DOCX 后缀看着像文档,实际上是一个 zip 压缩包,里面装的是 OpenXML 文本,浏览器根本不认识。想让 Word 文档在网页里显示,要么把 XML 解析成 HTML 放进去,要么走在线转换服务。

解析成 HTML 这条路可行,但选库要谨慎。mammoth 很轻量,但对复杂样式还原度低,表格、批注、复杂版式经常丢;docx-preview 直接在浏览器里执行渲染,还原度高很多,代价是包体积大、渲染出来的 DOM/canvas 节点多。先给结论:做真正的文件预览,选 docx-preview;如果只是提取正文内容给 AI 处理或者做搜索索引,再考虑 mammoth。

1.3 PPTX 才是真正的硬骨头,纯前端方案基本不现实

很多人以为 PPTX 和 DOCX 一样,找个库解析 XML 就能渲染。实际操作过就会发现完全不是一回事。PPTX 内部是绝对定位的画布模型:每个形状、图片、文本框都带坐标和尺寸,动画、渐变、SmartArt、母版字体、图表,各种效果叠加,纯 JS 解析的工程量不亚于做一个简化版 PowerPoint 渲染引擎。

目前开源的 pptxjs、pptx2html 等项目要么停更多年,要么还原度差到没法交付。工程上的出路只有两条:外网环境用微软或谷歌的在线预览服务,把文件公网 URL 拼到 iframe 里;内网环境部署 LibreOffice,把 PPTX 转成 PDF 再走 pdf.js 链路。这个"转换替代渲染"的思路,是 PPTX 预览唯一靠谱的落地姿势。

1.4 内外网环境决定方案上限,动手前必须问清楚

同样是预览,外网和内网的可用资源完全不同。外网可以引 CDN、可以调在线转换 API;内网可能连公网 DNS 都不通,所有渲染依赖都得本地化,第三方在线服务更是想都别想。我见过有人在内网项目里套网上的 CDN 方案,部署上线后预览按钮全部白屏,就是因为 pdf.js 和 docx-preview 的资源全在公网 CDN 上。

所以选型之前一定要把部署环境问清楚。我的原则是:能本地打包的库就本地打包,别赌浏览器和服务器一定能访问外网;必须依赖第三方服务的功能(比如 PPTX 在线预览),单独留一个开关,内外网构建时切换。

2. 环境准备:把 vue3 + vite 的底座搭对

2.1 为什么这里选 Vite 而不是 Webpack

vue3 项目初始化我直接用的 Vite。对文件预览这个场景,Vite 有两个 Webpack 很难替代的优势:一是开发服务器快,改代码秒级热更新;二是内置对?worker、?url这类静态资源参数的原生支持,pdf.js 的 Web Worker 在 Vite 里配置起来非常干净。如果用 Webpack,worker 和静态资源处理还要额外装 loader 配 rule,绕一大圈。

pnpm create vite my-preview-app --template vue-ts

2.2 依赖安装与版本注意点

pnpm add pdfjs-dist pnpm add docx-preview jszip pnpm add -D postcss-px-to-viewport

pdfjs-dist 建议锁定大版本,因为它的 API 在 v3 和 v4 之间存在细微差异,Worker 初始化方式也不同,网上很多教程混用版本导致各种诡异报错。docx-preview 依赖 jszip,安装时一起装上,防止某些包管理器没有自动解析依赖,后续在运行时才暴露 "Cannot find module" 这种问题。postcss-px-to-viewport 是给移动端适配用的,第6章会详细讲它的适用边界。

2.3 组件与目录规划

预览模块我拆成六个部分:

src/ views/Preview/index.vue # 预览页面入口 components/PreviewRouter.vue # 按后缀名分发组件 components/PdfPreview.vue # PDF 浏览器 components/DocxPreview.vue # Word 文档渲染 components/PptxPreview.vue # PPT 预览(外网 iframe / 内网转换) utils/file.ts # 文件类型判断、blob 获取

这样拆是为了让 PreviewRouter 只做一件事:判断文件后缀,返回对应组件。以后要再加 xlsx、txt 的预览,不用改入口页面,扩展成本非常低。每个预览组件各自独立,是因为三个文件类型的生命周期完全不同:PDF 和 PPTX 转出来的 PDF 需要管理 canvas 渲染,docx 需要处理 DOCX 解析和样式容器,混在一个组件里会把状态搞得非常乱。

2.4 环境变量文件先行

因为后面要做内外网切换,项目初始化时就把环境变量文件建好:

.env.external .env.internal

两个文件里放VITE_PREVIEW_MODE=external|internal、VITE_API_BASE这类变量,程序里统一用import.meta.env.VITE_xxx读取。这个习惯越早建立,后面第7章做构建分流越省事。很多团队项目都上线了还没有环境变量文件,临时要加一个内网包结果只能改代码再部署一次,非常被动。

3. PDF 预览落地:worker 配置是最容易翻车的一环

3.1 最简渲染链路:从 blob 到画布

pdf.js 的核心链路非常清晰:拿文件数据传给getDocument,得到 PDFDocumentProxy 对象,然后按页调用getPage和render。为了保证跨域和内网可用,我建议前端先 fetch 文件转成 ArrayBuffer 再传给 pdf.js,而不是直接把 URL 丢给它内部加载,因为内网文件服务跨域配置可能不全,直接依赖 pdf.js 内部加载会在 CORS 上踩更多坑。

import * as pdfjsLib from 'pdfjs-dist' const loadingTask = pdfjsLib.getDocument({ data: arrayBuffer }) const pdfDoc = await loadingTask.promise

单页渲染:

async function renderPage(pdfDoc: PDFDocumentProxy, pageNum: number, scale: number) { const page = await pdfDoc.getPage(pageNum) const viewport = page.getViewport({ scale }) const canvas = canvasRef.value! canvas.width = viewport.width canvas.height = viewport.height const ctx = canvas.getContext('2d')! await page.render({ canvasContext: ctx, viewport }).promise }

3.2 Vite 里 Worker 配置的几种正确姿势

pdf.js 渲染依赖 Web Worker,否则大 PDF 的页面缩放、翻页会明显卡顿。Worker 在 Vite 里怎么配置,是新手最容易翻车的地方。我试下来有两种可靠写法,完全等价,按团队习惯选一种即可。

第一种是用?worker后缀,让 Vite 把 Worker 文件单独打包,并返回一个构造函数:

import PdfWorker from 'pdfjs-dist/build/pdf.worker.min.js?worker' pdfjsLib.GlobalWorkerOptions.workerPort = new PdfWorker()

第二种是用?url拿到打包后的文件地址,再赋给workerSrc:

import workerUrl from 'pdfjs-dist/build/pdf.worker.min.js?url' pdfjsLib.GlobalWorkerOptions.workerSrc = workerUrl

两种写法不要混用,也不要直接import 'pdfjs-dist/build/pdf.worker.min.js',那会试图在主线程执行 worker 文件,必然报Invalid or unexpected token之类的语法错误。网上很多 pdf.js 引入报错帖,十有八九是这一步出了问题。

外网场景下,为了让首屏更轻,可以把 worker 指向公网 CDN:

pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.11.174/build/pdf.worker.min.js'

内网场景就用?url让它打进本地静态资源。这也是"内外网差异"在第一个技术点上的具体体现,后面第7章会用构建模式把这两种场景做成自动化分流。

3.3 翻页、缩放、旋转的状态管理

预览器本质上是一个"当前页码 + 缩放比例"的状态机。我在 PdfPreview.vue 里维护pageNum和scale,每次变化重新调用 renderPage。渲染前要先清理 canvas,渲染调用链上要拿到 Promise 并 await,避免连续快速翻页时上一次异步渲染覆盖掉下一次结果。页码边界用pdfDoc.numPages兜住,防止越界。

缩放实现推荐调整scale后重新渲染 canvas,而不是用 CSS transform 直接放大。CSS 放大 canvas 会把文字弄得模糊,而 pdf.js 重新渲染一次实际像素,清晰度完全不一样。可以加一层节流,scale 变化时 200ms 内只触发一次重绘,移动端低性能设备上的体验会明显更顺滑。

3.4 大文件与内存:冷启动慢比白屏好

PDF 越大,getDocument 阶段越慢,扫描版文件经常几十上百 MB。我的处理策略是先展示 loading 状态,显示文件大小和加载进度,超过 20 秒给出"文件过大,建议下载后查看"的提示,而不是让页面一直转圈。

另外,页面销毁时一定要执行pdfDoc.destroy(),否则 Web Worker 和 canvas 的 GPU 内存不会立刻释放。用户在预览列表里连续打开几个大 PDF,浏览器内存会肉眼可见地涨,移动端这里尤其敏感。这个习惯属于典型的"不报错但不做就出事"的细节,建议写进团队的代码规范。

4. DOCX 预览落地:docx-preview 是还原度最优解

4.1 为什么放弃 mammoth

mammoth 的定位是"把 Word 转成干净的 HTML",内部做的是有损转换,正文文字基本能出来,但复杂的表格宽度、合并单元格、页眉页脚、批注、修订痕迹经常丢或者错位。如果需求只是"给后台运营看内容概要"也许够用,但做正式文件预览,用户拿 Word 原文件和页面一对比,发现差异就会来反馈。

docx-preview 是直接把 OpenXML 解析成页面结构,用 canvas 把看到的 Word 页面画出来,段落、表格、图片、页码的还原度都明显高一个档次。代价是包体积大、DOM 节点多,但文件预览正确性优先,这个代价值得。

4.2 接入代码与容器要求

import { renderAsync } from 'docx-preview' const res = await fetch(url) const blob = await res.blob() const container = docxContainerRef.value! await renderAsync(blob, container, container, { className: 'docx-preview', inWrapper: true, ignoreWidth: false, ignoreHeight: false, })

renderAsync 的第三个参数是"样式作用容器",通常直接传同一个 container。如果你用 axios 获取文件,记得设置responseType: 'blob',否则文件会被解析成 JSON 字符串,渲染直接失败。容器的高度建议设成自适应内部内容而不是写死 100%,外层再用 overflow: auto 提供滚动,这样文档多长都能浏览。

4.3 图片、字体、表格的还原经验

docx 里内嵌的图片会随 blob 一并解析,基本不用额外处理。真正的坑在字体:docx 指定了宋体、微软雅黑这类字体,如果预览端操作系统没有安装,浏览器会回退到默认字体,排版肉眼可见地变松。内网系统尤其常见,因为很多服务器/瘦客户机没装全量中文字体。遇到后可以用@font-face引入一套常用中文字体,但字体文件体积很大,要根据实际文档使用情况决定是否全面引入。

表格是另一个重灾区。docx-preview 的ignoreWidth和ignoreHeight配置默认 false,意思是严格按文档宽度渲染。我建议保持 false,因为 true 虽然能适配窄屏手机,但会把表格挤得很难看,后台系统桌面端体验会打折扣。移动端给表格外层容器加overflow-x: auto保底。

4.4 超长文档的性能问题

一份 100 页以上的 Word,渲染出来的节点数量非常可观,手机上低端机可能直接卡到 10fps 以下。我实测过,50 页普通文档在 iPhone 上滚动尚可,100 页带大量表格的文档就明显掉帧。折中方案是分页渲染,用分页参数或者自己按高度懒加载后续页面,但实现成本不低。

如果业务场景确实频繁预览超长文档,我建议后端额外做一道 PDF 转换,方案和第5章 PPTX 内网方案一样用 LibreOffice,前端走 PDF 预览,性能比纯 DOM 渲染稳定得多。技术方案没有银弹,文档类型和体量分布决定了你要在纯前端优化上花多大力气。

5. PPTX 预览落地:外网走在线服务,内网只能转 PDF

5.1 PPTX 内部结构决定了"渲染"不可行

PPTX 和 DOCX 一样是 zip 加 OpenXML,但结构完全不同。DOCX 是流式文档,段落从上到下排列,解析成可滚动页面相对可行;PPTX 是画布模型,每张 slide 里是几十个绝对定位的形状节点,每个形状有 x、y、cx、cy 坐标,真正的视觉效果还依赖母版、主题、字体替换、渐变、动画。纯前端还原,等于要重做一个 PowerPoint 渲染引擎。

这也是为什么 PPTX 的预览方案在行业里基本没有纯开源答案。我见过一些团队自己做简易渲染,最后只能显示文字和图片,稍微复杂点的模板就乱了,交付给业务方验收根本过不了。所以正确思路是:不要试图渲染 PPTX,把它转换成 PDF,或者用现成在线引擎解析,然后复用成熟的 PDF/iframe 链路。

5.2 外网方案:微软和谷歌的在线预览 iframe

如果文件服务部署在外网,文件有公网可访问的 URL,最简单的是用它拼接在线预览服务:

<template> <iframe :src="officeViewerSrc" style="width: 100%; height: 100%; border: 0" /> </template> <script setup lang="ts"> import { computed } from 'vue' const props = defineProps<{ fileUrl: string }>() const officeViewerSrc = computed(() => { return `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(props.fileUrl)}` }) </script>

谷歌的链接格式类似:https://docs.google.com/gview?url=${encodeURIComponent(fileUrl)}

这个方案的好处是零依赖、还原度极高,因为背后是微软或谷歌的官方渲染引擎;坏处也很明显:文件必须能公网访问,第三方服务的可用性不受你控制,不同网络环境下访问这两个服务的稳定性需要实测,不能想当然。如果只是内网系统,这个方案直接不成立。

5.3 内网方案:LibreOffice headless 转 PDF

内网想要还原度高的 PPTX 预览,最可靠的是在后端部署 LibreOffice,把 PPTX 转成 PDF,前端复用第3章的 PDF 预览链路。后端核心就是一条命令:

soffice --headless --convert-to pdf --outdir /data/convert/ input.pptx

在 Node 服务里用 child_process 封装一层:

import { execFile } from 'node:child_process' async function convertPptxToPdf(inputPath: string, outputDir: string) { return new Promise((resolve, reject) => { execFile( 'soffice', ['--headless', '--convert-to', 'pdf', '--outdir', outputDir, inputPath], (err, stdout) => (err ? reject(err) : resolve(stdout)), ) }) }

这里有几个服务器运维经验要分享:soffice 进程对并发处理支持很差,两个转换任务同时来会互相阻塞甚至报错,后端要做一个简单的任务队列;首次冷启动很慢,可能要 3 到 5 秒加载组件库,后续转换会快一些;转换过程内存占用高,容器内存别给太少,建议 2GB 起步,否则批量转换时进程可能被 OOM kill 掉。

5.4 前端按模式分流

PptxPreview 组件内部不直接决定用哪个方案,而是读环境变量:

const isExternal = import.meta.env.VITE_PREVIEW_MODE === 'external' const officeViewerSrc = computed(() => { if (isExternal) { return `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(props.fileUrl)}` } return '' }) async function loadInternalPreview() { const res = await fetch(`/api/preview/convert?url=${encodeURIComponent(props.fileUrl)}`) const blob = await res.blob() // 把 blob 交给 PdfPreview 组件的 source }

外网用户打开是 iframe 在线预览,内网用户打开是转好的 PDF,体验链路统一,但底层完全不同。这也是整个内外网方案里最关键的分岔逻辑。外网如果不想用第三方服务,也可以用 LibreOffice 转换方案,只是需要多部署一个转换服务。

6. 移动端适配:布局、手势与 canvas 的联动改造

6.1 移动端三大痛点

移动端预览的坑集中在这三块:第一,桌面网页塞进手机,视口缩得跟邮票一样大,字小得点不到;第二,触摸交互和鼠标不一样,单指滑动、双指缩放、工具栏点击都要自己处理;第三,内存和 GPU 比桌面机弱,大 PDF 和大 DOCX 分分钟白屏或卡死。

如果直接把桌面版预览组件塞到手机,用户会得出一个结论:别用手机看。但业务方既然提了"移动端适配",就说明这个场景躲不掉,只能从布局、手势、内存三个方向逐个解决。

6.2 视口和布局:100dvh 是个容易被忽略的细节

预览页最外层容器用height: 100dvh而不是 100vh,区别在于移动端浏览器地址栏和底部手势条会动态改变可视高度,100vh 在 iPhone 上底部会被手势条遮住。dvh 是动态视口单位,会跟着浏览器 UI 伸缩,底部工具栏就不会被手势条挡住。

工具栏固定顶部,翻页按钮区固定底部,中间留一个可以滑动的预览区。所有可点击控件的触摸区域要大于 44px,这是苹果 HIG 和安卓 Material 设计规范里的建议值。我后来把手动设置的按钮改成了 min-width 和 min-height 兜底,点按误触率明显下降。

6.3 给 PDF/PPT 加手势:单指翻页、双指缩放

PDF 预览器在移动端需要的交互和桌面端完全不一样。桌面端是滚动加滚轮缩放,移动端最自然的操作是:单指左右滑动切页,双指缩放调整页面大小。

实现逻辑不算复杂,touchstart 记录起始坐标和初始缩放值,touchmove 里判断两点距离变化得出缩放比例,touchend 时如果只是小位移单击则忽略,位移超过阈值就翻页。基础代码如下:

let startX = 0 let startDistance = 0 let startScale = 1 function onTouchStart(e: TouchEvent) { if (e.touches.length === 1) { startX = e.touches[0].clientX } else if (e.touches.length === 2) { startDistance = Math.hypot( e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY, ) startScale = scale.value } } function onTouchMove(e: TouchEvent) { if (e.touches.length === 2) { const d = Math.hypot( e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY, ) scale.value = Math.min(4, Math.max(0.5, startScale * (d / startDistance))) } }

实现后要在容器 CSS 上写touch-action: pan-y pinch-zoom,避免浏览器默认行为抢走手势。注意:canvas 的缩放要重新用 pdf.js 渲染,不能只靠 CSS transform,否则文字会糊,这点在第3章说过,移动端更明显,因为手机屏幕 PPI 高,位图被放大一点点都能看出来。

6.4 postcss-px-to-viewport 的适用边界

借助 postcss-px-to-viewport 可以把设计稿的 px 自动转 vw,让整个后台管理系统的移动端样式整体等比缩放,这是很多团队做移动端适配的惯用手段。完整配置如下:

// postcss.config.cjs module.exports = { plugins: { 'postcss-px-to-viewport': { viewportWidth: 390, unitPrecision: 5, viewportUnit: 'vw', fontViewportUnit: 'vw', minPixelValue: 1, exclude: [/node_modules/], }, }, }

这里要泼一盆冷水:postcss 只能处理写进 CSS 的 px,canvas 里的页面尺寸是 JS 控制的,postcss 管不到。用 pdf.js 渲染时,如果宿主页面用 vw 缩放了,canvas 还是按像素尺寸画,两边就会出现不一致。处理方法是给 PDF 预览容器单独设定max-width: 100%,canvas 只根据实际容器像素宽度计算缩放比例,不要把 canvas 塞进 vw 布局里。这个坑的原理和"pxtorem 对 echarts 没起到效果"完全一样:图表库内部 canvas 尺寸不受 CSS 插件控制。

6.5 移动端 docx 的滚动与缩放

docx-preview 渲染出的内容本质上是 canvas 绘制,和 PDF 类似,不是天然可滚动的 HTML。移动端想浏览长文档,不能指望浏览器原生滚动行为,要在外层容器维护滚动或分页视图,并把外层容器高度设为动态视口单位,让内容按页浏览。

字体已经很小的情况下,不建议加缩放按钮,容易和页面滚动冲突。如果明确有这个需求,可以在预览容器上做双击缩放,但要注意 transform 后内部文本选择、点击位置会偏移,体验打折扣。绝大多数场景,按页浏览加手势翻页已经够用,把交互做重不如做稳。

7. 内外网构建切换与实测:build mode 才是真正的管理开关

7.1 两种构建模式怎么配

内外网差异本质是环境变量差异,我用构建模式区分。项目的 package.json scripts 这样写:

{ "scripts": { "build:external": "vite build --mode external", "build:internal": "vite build --mode internal" } }

然后在.env.external和.env.internal里写不同的配置:

# .env.internal VITE_PREVIEW_MODE=internal VITE_API_BASE=/internal-api
# .env.external VITE_PREVIEW_MODE=external VITE_API_BASE=/api

构建时 Vite 会自动加载对应模式的环境变量。程序里所有跟内外网相关的分支,都通过import.meta.env.VITE_PREVIEW_MODE读取,不要自己再猜环境。这个习惯从我第一次因为混淆环境导致线上预览全挂之后,就再也不敢省了。

7.2 按需加载与分包:别让首屏扛下所有

pdfjs-dist 和 docx-preview 打包出来都不小,如果把三个预览组件都静态 import,首屏 JS 会非常臃肿。正确做法是用 defineAsyncComponent 按文件后缀懒加载:

const PdfPreview = defineAsyncComponent(() => import('./PdfPreview.vue')) const DocxPreview = defineAsyncComponent(() => import('./DocxPreview.vue')) const PptxPreview = defineAsyncComponent(() => import('./PptxPreview.vue'))

同时在 vite.config 里做 manualChunks,把大的基础库单独拆文件,方便浏览器长缓存:

build: { rollupOptions: { output: { manualChunks: { pdf: ['pdfjs-dist'], docx: ['docx-preview'], }, }, }, },

实测下来,外网模式下 PDF chunk 和 DOCX chunk 都是按需加载的,未预览文档时首页 JS 体积能少一半以上。内网模式虽然资源都打在本地方便,但由于静态资源走本地服务,加载速度通常反而比外网 CDN 更容易稳定,因为不需要做证书校验、跨地域回源这些不可控环节。

7.3 取舍与实测观察(含踩坑)

最后分享几个真实观察。文档预览在后台管理系统里很常见,但纯前端方案总会有边界。我把三类文件的预览链路上线后跑了半个月,最深的体会有三个:

第一,pdf.js 版本要锁死。v3 和 v4 的 Worker 配置、部分 API 名称都有差异,升级大版本前先在测试环境把各类 PDF 都过一遍再上线。依赖不升级不是懒,是稳定优先。

第二,移动端的任务不是说"能用"就结束,要让业务方把手伸到真实手机上滑一滑。预览界面在 Android WebView 里出现过两个问题:一个是大 PDF 渲染过程中切后台,回来 canvas 黑屏,需要在 visibilitychange 事件里重新渲染当前页;另一个是 docx 渲染大量表格时,某些国产手机浏览器直接崩溃,后来加了"超大文档转 PDF 预览"的兜底逻辑才解决。

第三,内外网构建模式这种功能,逻辑上只影响几行代码,但部署上必须写清楚。我在内部文档里明确标注:外网包走在线预览,内网包走本地渲染加转换服务,避免运维同事用错构建包。

如果后续业务里出现了 xlsx、csv 在线预览,方案也可以沿用这套框架:xlsx 用 SheetJS 解析成表格,csv 直接文本解析,新加一个 PreviewXlsx 组件,在 PreviewRouter 里加一个 case 即可;需要转 PDF 的统一走 LibreOffice 转换服务。这套"路由加懒加载加环境变量分流"的架子,撑三五年没有问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询