Отображение PDF в приложениях Vue 3
Встраивание PDF в Vue 3 SPA звучит просто — пока вы не столкнётесь с первой ошибкой CORS, не обнаружите, что браузер отображает его по-разному в Safari, или не поймёте, что ваш 200-страничный документ блокирует основной поток. В этой статье рассматриваются три практических подхода, которые разработчики реально используют для отображения PDF в Vue 3, с честными компромиссами для каждого.
Ключевые выводы
- Три основных подхода для отображения PDF в Vue 3 — это нативные браузерные встраивания (
<iframe>/<embed>), прямая интеграция PDF.js и Vue-специфичные компоненты-обёртки. - Нативные встраивания не увеличивают размер бандла, но не дают контроля над стилизацией, панелями инструментов или кроссбраузерной совместимостью.
- PDF.js предоставляет полный контроль над рендерингом, но добавляет заметную нагрузку на клиентскую сторону — используйте ленивую загрузку с динамическим
import(), чтобы сохранить быструю начальную загрузку. - Vue PDF обёртки, такие как
vue-pdf-embed, значительно сокращают шаблонный код за счёт некоторой гибкости кастомизации. - Правильная настройка воркера и ленивая загрузка — это два шага, которые предотвращают наиболее распространённые проблемы, связанные с PDF в приложениях Vue.
Три основных подхода
1. Нативное браузерное встраивание: <iframe> или <embed>
Самый быстрый способ отобразить PDF на экране — передать его встроенному в браузер рендереру PDF.
<template>
<iframe
:src="pdfUrl"
width="100%"
height="700px"
style="border: none"
/>
</template>
<script setup lang="ts">
const pdfUrl = '/documents/report.pdf'
</script>
Когда это работает хорошо: Внутренние инструменты, административные панели или любой контекст, где согласованность UI не критична, а PDF обслуживается с того же источника.
Реальные ограничения:
- Нулевой контроль над панелью инструментов, элементами управления масштабированием или темизацией
- Поведение различается в разных браузерах — Chrome, Firefox и Safari рендерят по-разному
- Мобильные браузеры часто скачивают файл вместо отображения его встроенным образом
- Состояние загрузки невидимо для вашего Vue-компонента, поэтому вы не можете показать спиннер или корректно обработать ошибки
Если вы загружаете PDF с другого домена, браузер заблокирует запрос, если сервер не отправит корректные заголовки Access-Control-Allow-Origin. Прокси-эндпоинт с тем же источником на вашем бэкенде — самое чистое решение.
2. PDF.js с Vue 3: прямая интеграция
PDF.js — это движок рендеринга PDF с открытым исходным кодом от Mozilla. Начиная с серии релизов 5.x, он поставляется как ESM-first пакет. Вы импортируете из build/pdf.mjs и загружаете отдельный воркер (pdf.worker.mjs), чтобы рендеринг не блокировал основной поток.
npm install pdfjs-dist
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import * as pdfjsLib from 'pdfjs-dist/build/pdf.mjs'
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.mjs',
import.meta.url
).toString()
const canvasRef = ref<HTMLCanvasElement | null>(null)
onMounted(async () => {
const pdf = await pdfjsLib.getDocument('/documents/report.pdf').promise
const page = await pdf.getPage(1)
const viewport = page.getViewport({ scale: 1.5 })
const canvas = canvasRef.value!
const ctx = canvas.getContext('2d')!
canvas.height = viewport.height
canvas.width = viewport.width
await page.render({ canvasContext: ctx, viewport }).promise
})
</script>
<template>
<canvas ref="canvasRef" />
</template>
Что вы получаете: Полный контроль над рендерингом, навигацией по страницам, масштабированием и темизацией. PDF.js поддерживает стандартные аннотации и поля AcroForm, хотя поведение форм и аннотаций может зависеть от конфигурации рендеринга.
Что вам следует знать:
- Пакет
pdfjs-distдобавляет заметную нагрузку к вашему клиентскому бандлу. Используйте динамическийimport()для ленивой загрузки. - Формы на основе XFA (распространённые в старых государственных PDF) имеют ограниченную поддержку и могут отображаться некорректно.
- Для больших документов PDF.js может загружать PDF частями, когда сервер поддерживает HTTP range-запросы (
Accept-Ranges: bytes), что может улучшить воспринимаемую производительность для больших файлов. - Конфигурация воркера — наиболее распространённая проблема настройки в проектах Vite. Паттерн
new URL(..., import.meta.url), показанный выше, корректно разрешает путь к воркеру во время сборки.
Discover how at OpenReplay.com.
3. Vue PDF обёртки
Несколько поддерживаемых Vue 3 компонентов оборачивают PDF.js и предоставляют более простой компонентный API. vue-pdf-embed — один из активно поддерживаемых вариантов, который обрабатывает настройку воркера, рендеринг страниц и реактивные обновления пропсов за вас.
<script setup lang="ts">
import VuePdfEmbed from 'vue-pdf-embed'
</script>
<template>
<VuePdfEmbed source="/documents/report.pdf" />
</template>
Компромисс заключается в меньшем детальном контроле в обмен на значительно меньший объём шаблонного кода. Эти обёртки хорошо подходят, когда вам нужен рабочий Vue 3 PDF-просмотрщик быстро и вам не нужно кастомизировать конвейер рендеринга.
Примечание: Некоторые старые пакеты, такие как
vue3-pdf-app, больше не поддерживаются активно. Оцените статус поддержки и совместимость с современными бандлерами, такими как Vite, прежде чем принимать какую-либо обёртку.
Выбор правильного подхода
| Подход | Стоимость бандла | Кастомизация | Лучше всего для |
|---|---|---|---|
<iframe> / <embed> | 0 КБ | Отсутствует | Быстрые встраивания, файлы с того же источника |
| Прямой PDF.js | Нагрузка на клиента | Полная | Кастомные просмотрщики, большие документы |
| Vue обёртка | Аналогично PDF.js | Умеренная | Быстрая настройка, стандартные случаи |
Заключение
Для большинства приложений Vue 3 решение простое: используйте <iframe> для простых встраиваний с того же источника, обратитесь к поддерживаемой Vue обёртке, когда вам нужен рабочий просмотрщик без особой настройки, и интегрируйте PDF.js напрямую, когда вам нужен полный контроль над рендерингом, навигацией или производительностью для больших документов. Какой бы путь вы ни выбрали, правильно настройте воркер PDF.js и используйте ленивую загрузку библиотеки. Эти два шага сами по себе предотвратят наиболее распространённые проблемы, с которыми сталкиваются разработчики при встраивании PDF в приложения Vue.
Часто задаваемые вопросы
Браузер блокирует кросс-доменные запросы PDF, если сервер не включает заголовок Access-Control-Allow-Origin в свой ответ. Наиболее надёжное решение — настроить прокси-эндпоинт с тем же источником на вашем собственном бэкенде, который получает PDF и передаёт его вашему Vue-приложению. Это позволяет избежать зависимости от конфигураций сторонних серверов, которые вы можете не контролировать.
Vite обрабатывает URL ресурсов иначе, чем Webpack. Используйте паттерн new URL с import.meta.url для разрешения пути к воркеру во время сборки. Например, установите workerSrc в new URL для pdfjs-dist/build/pdf.worker.mjs с import.meta.url, затем вызовите toString. Это гарантирует, что Vite корректно обработает файл воркера как во время разработки, так и в продакшн-сборках.
Да. После загрузки документа пройдите циклом от 1 до pdf.numPages, вызовите getPage для каждого номера страницы, создайте элемент canvas для каждой и отрендерьте их последовательно или параллельно. Для очень больших документов рассмотрите рендеринг только видимых страниц и загрузку остальных при прокрутке, чтобы избежать высокого потребления памяти и медленного начального рендеринга.
Используйте обёртку, такую как vue-pdf-embed, если вам нужен стандартный просмотрщик с минимальной настройкой и вам не требуется глубокая кастомизация. Интегрируйте PDF.js напрямую, когда вам нужен полный контроль над рендерингом, кастомной навигацией, темизацией или оптимизацией производительности, такой как ленивая загрузка страниц. Обёртки добавляют тонкий слой поверх PDF.js, поэтому стоимость бандла практически идентична в любом случае.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.