Back

Vue 3アプリケーションでのPDF表示

Vue 3アプリケーションでのPDF表示

Vue 3 SPAにPDFを埋め込むのは簡単に聞こえますが、最初のCORSエラーに遭遇したり、Safariでのブラウザレンダリングが異なることに気づいたり、200ページのドキュメントがメインスレッドをブロックしていることに気づくまでの話です。この記事では、開発者が実際にVue 3でPDFを表示するために使用する3つの実用的なアプローチを、それぞれの正直なトレードオフとともに解説します。

重要なポイント

  • Vue 3でPDFを表示する主な3つのアプローチは、ネイティブブラウザ埋め込み(<iframe>/<embed>)、PDF.jsの直接統合、Vue専用ラッパーコンポーネントです。
  • ネイティブ埋め込みはバンドルサイズがゼロですが、スタイリング、ツールバー、クロスブラウザの一貫性に対する制御はありません。
  • PDF.jsは完全なレンダリング制御を提供しますが、クライアント側のペイロードが顕著に増加します。動的なimport()で遅延ロードすることで、初期ロード時間を高速に保つことができます。
  • vue-pdf-embedのようなVue PDFラッパーは、カスタマイズの柔軟性と引き換えに、ボイラープレートを大幅に削減します。
  • 正しいワーカー設定と遅延ロードは、VueアプリでPDF関連の最も一般的な問題を防ぐ2つのステップです。

3つの主要なアプローチ

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はMozillaのオープンソースPDFレンダリングエンジンです。5.xリリースシリーズ以降、ESMファーストパッケージとして提供されています。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で一般的)はサポートが限定的で、正しくレンダリングされない場合があります。
  • 大きなドキュメントの場合、サーバーがHTTP範囲リクエスト(Accept-Ranges: bytes)をサポートしていれば、PDF.jsはPDFをチャンクで取得でき、大きなファイルの体感パフォーマンスを向上させることができます。
  • ワーカー設定は、Viteプロジェクトで最も一般的なセットアップの問題です。上記のnew URL(..., import.meta.url)パターンは、ビルド時にワーカーパスを正しく解決します。

3. Vue PDFラッパー

いくつかのメンテナンスされているVue 3コンポーネントがPDF.jsをラップし、よりシンプルなコンポーネントAPIを公開しています。vue-pdf-embedは、ワーカーのセットアップ、ページレンダリング、リアクティブなプロップ更新を処理してくれる、アクティブにメンテナンスされているオプションの1つです。

<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 KBなし簡単な埋め込み、同一オリジンファイル
直接PDF.js統合クライアント側ペイロード完全カスタムビューア、大きなドキュメント
VueラッパーPDF.jsと同程度中程度より速いセットアップ、標準的なユースケース

まとめ

ほとんどのVue 3アプリケーションでは、決定は簡単です。シンプルな同一オリジン埋め込みには<iframe>を使用し、多くのセットアップなしで動作するビューアが必要な場合はメンテナンスされているVueラッパーを使用し、大きなドキュメントのレンダリング、ナビゲーション、パフォーマンスを完全に制御する必要がある場合はPDF.jsを直接統合してください。どの道を選んでも、PDF.jsワーカーを正しく設定し、ライブラリを遅延ロードしてください。この2つのステップだけで、開発者がVueアプリにPDFを埋め込む際に遭遇する最も一般的な問題を防ぐことができます。

よくある質問

サーバーがレスポンスにAccess-Control-Allow-Originヘッダーを含めない限り、ブラウザはクロスオリジンのPDFリクエストをブロックします。最も信頼性の高い解決策は、独自のバックエンドに同一オリジンのプロキシエンドポイントを設定し、PDFを取得してVueアプリに提供することです。これにより、制御できない可能性のあるサードパーティサーバーの設定に依存することを避けられます。

ViteはWebpackとは異なる方法でアセットURLを処理します。ビルド時にワーカーパスを解決するには、import.meta.urlを使用したnew URLパターンを使用してください。例えば、workerSrcをimport.meta.urlを使用したpdfjs-dist/build/pdf.worker.mjsのnew 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.

OpenReplay