所有分类
  • 所有分类
  • Html5资源
  • React资源
  • Vue资源
  • Php资源
  • ‌小程序资源
  • Python资源

Vue 3 实战:从零到一用 vue-pdf-embed 打造功能齐全的 PDF 查看器

你好,Vue 开发者们!

在 Web 开发中,我们经常会遇到需要在页面中直接展示 PDF 文件的需求,例如预览合同、显示报告或在线阅读文档。你可能会想到用 <iframe> 或者一些重量级的库,但它们往往不够灵活或过于臃肿。

今天,我将向你介绍一个轻量、强大且对 Vue 3 非常友好的解决方案——vue-pdf-embed

🤔 vue-pdf-embed 是什么?

vue-pdf-embed 是一个专门为 Vue 设计的 PDF 查看器组件。它基于 Mozilla 的 PDF.js,但去除了所有不必要的 UI 和复杂性,只专注于提供一个纯粹、高性能的 PDF 渲染核心。

核心亮点:

  • 📦 轻量级:只包含渲染 PDF 所需的核心逻辑,打包体积小。
  • 🖼️ 高质量渲染:利用 PDF.js,确保 PDF 内容清晰、准确地显示。
  • 📱 响应式:能够很好地适应不同尺寸的容器。
  • 🔧 高度可控:通过 Props 和事件,你可以完全控制 PDF 的渲染行为,如页码、缩放、旋转等。
  • Vue 3 兼容:完美支持 Vue 3 的组合式 API 和 <script setup> 语法。

🛠️ 安装

vue-pdf-embed 添加到你的项目中非常简单:

npm install vue-pdf-embed

🚀 快速上手:三分钟渲染你的第一个 PDF

集成 vue-pdf-embed 只需要几行代码。

1. 引入组件

<script setup>
import VuePdfEmbed from "vue-pdf-embed";
</script>

2. 在模板中使用

你需要提供一个 source 属性,指向你的 PDF 文件。它可以是一个 URL、Base64 字符串或 ArrayBuffer。

<template>
  <VuePdfEmbed
    source="https://raw.githubusercontent.com/mozilla/pdf.js/master/web/compressed.tracemonkey-pldi-09.pdf"
  />
</template>

就这样!一个 PDF 查看器已经成功渲染在你的页面上了。是不是超级简单?


✨ 进阶实战:打造一个功能齐全的 PDF 查看器

当然,仅仅显示 PDF 是不够的。我们通常需要分页、缩放等功能。下面,我们将创建一个更完整的 PDF 查看器组件。

核心思路

  1. 加载 PDF:监听 @loaded 事件,在 PDF 加载完成后获取总页数。
  2. 分页控制:创建“上一页”和“下一页”按钮,通过修改 page prop 来切换页面。
  3. 缩放控制:创建“放大”和“缩小”按钮,通过修改 scale prop 来调整视图大小。
  4. 状态显示:显示当前页码和总页数。

完整代码示例 (PdfViewer.vue)

<template>
  <div class="pdf-viewer-container">
    <!-- 控制栏 -->
    <div class="controls">
      <button @click="prevPage" :disabled="page <= 1">上一页</button>
      <span>第 {{ page }} / {{ pageCount }} 页</span>
      <button @click="nextPage" :disabled="page >= pageCount">下一页</button>

      <button @click="zoomOut" :disabled="scale <= 0.5">缩小</button>
      <span>缩放: {{ Math.round(scale * 100) }}%</span>
      <button @click="zoomIn" :disabled="scale >= 2">放大</button>
    </div>

    <!-- PDF 渲染区域 -->
    <div class="pdf-wrapper">
      <VuePdfEmbed
        ref="pdfRef"
        :source="pdfSource"
        :page="page"
        :scale="scale"
        @loaded="handleDocumentLoaded"
        @rendering-failed="handleRenderingFailed"
      />
    </div>
  </div>
</template>

<script setup>
import { ref, watch } from "vue";
import VuePdfEmbed from "vue-pdf-embed";

// PDF 文件源
const pdfSource = ref(
  "https://raw.githubusercontent.com/mozilla/pdf.js/master/web/compressed.tracemonkey-pldi-09.pdf"
);

// PDF 组件引用
const pdfRef = ref(null);

// PDF 状态
const page = ref(1);
const pageCount = ref(0);
const scale = ref(1);

// PDF 加载完成回调
function handleDocumentLoaded(pdf) {
  console.log("PDF 加载完成!", pdf);
  pageCount.value = pdf.numPages;
}

// PDF 渲染失败回调
function handleRenderingFailed(error) {
  console.error("PDF 渲染失败:", error);
}

// 分页功能
function prevPage() {
  if (page.value > 1) {
    page.value--;
  }
}

function nextPage() {
  if (page.value < pageCount.value) {
    page.value++;
  }
}

// 缩放功能
function zoomIn() {
  if (scale.value < 2) {
    scale.value += 0.25;
  }
}

function zoomOut() {
  if (scale.value > 0.5) {
    scale.value -= 0.25;
  }
}

// 监听页码变化,确保在范围内
watch(page, (newPage) => {
  if (newPage < 1) {
    page.value = 1;
  }
  if (newPage > pageCount.value && pageCount.value > 0) {
    page.value = pageCount.value;
  }
});
</script>

<style scoped>
.pdf-viewer-container {
  max-width: 900px;
  margin: 2rem auto;
  border: 1px solid #ccc;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.controls {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 1rem;
  padding: 1rem;
  background-color: #f5f5f5;
  border-bottom: 1px solid #ccc;
  border-radius: 8px 8px 0 0;
}

.controls button {
  padding: 0.5rem 1rem;
  border: 1px solid #ddd;
  border-radius: 4px;
  background-color: #fff;
  cursor: pointer;
}

.controls button:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

.controls span {
  font-size: 0.9rem;
  min-width: 100px;
  text-align: center;
}

.pdf-wrapper {
  height: 70vh;
  overflow: auto;
  background-color: #e9e9e9;
  padding: 1rem;
}

/* vue-pdf-embed 的内部样式,我们可以覆盖它 */
:deep(.vue-pdf-embed > div) {
  margin-bottom: 8px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
</style>

常用 Props 和事件

  • Props:
  • source: PDF 文件来源 (URL, Base64, Uint8Array, …)。
  • page: (Number) 要显示的页码。
  • scale: (Number) 缩放比例。
  • rotation: (Number) 旋转角度 (0, 90, 180, 270)。
  • width: (Number | String) 容器宽度。
  • height: (Number | String) 容器高度。
  • Events:
  • @loaded: PDF 文档加载完成时触发,回调参数为 PDF.js 的文档对象,可以从中获取总页数 numPages 等信息。
  • @rendered: 所有可见页面渲染完成时触发。
  • @rendering-failed: 渲染失败时触发。
  • @password-requested: 当 PDF 需要密码时触发。

总结

vue-pdf-embed 为在 Vue 应用中嵌入 PDF 提供了一个极其简单而又强大的解决方案。它避免了 <iframe> 的笨重和同源策略问题,也比一些重量级 UI 库更轻量、更灵活。

通过组合其 Props 和事件,你可以轻松构建出符合业务需求的、交互丰富的 PDF 查看器。如果你正在寻找一个可靠的 Vue PDF 渲染方案,vue-pdf-embed 绝对是你的不二之选。

原文链接:https://code.ifrontend.net/archives/1218,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?