跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
TypeScript大前端

基于 html2canvas 和 jsPDF 封装 Vue3 页面导出 PDF 工具

综述由AI生成一种基于 Vue3 和 TypeScript 封装的页面导出 PDF 方案。通过组合 html2canvas 和 jspdf 库,实现了将 DOM 元素转换为 PDF 文件的功能。文章详细分析了实现过程中的三大痛点:导出内容不全、图片模糊及 PDF 分页问题,并提供了相应的解决方案,如设置 windowHeight 避免滚动截断、scale 提升清晰度、循环 addPage 实现自动分页。此外,还提供了完整的 TypeScript Hook 代码示例及组件调用方法,并解释了 Canvas 导出时的跨域原理及 CORS 配置要求。该方案适用于需要将网页详情或报表导出为 PDF 的场景。

魔尊发布于 2026/3/21更新于 2026/5/2025 浏览
基于 html2canvas 和 jsPDF 封装 Vue3 页面导出 PDF 工具

封装好用的页面导出 PDF 工具 Hook (html2canvas + jspdf)

在最近的一个项目中,遇到将页面内容(详情页)导出为 PDF 的需求。目前似乎没有直接将 DOM 转为 PDF 的一步到位技术,因此封装了一个间接转换的方法。基于 Vue3 + TypeScript 的通用 Hook 封装,利用 html2canvas 和 jspdf 实现网页内容导出为 PDF,并解决了滚动截断、清晰度不足以及自动分页等常见问题。

一、技术选型

  • html2canvas: 将 DOM 元素转换为 Canvas 图片。
  • jspdf: 将 Canvas 图片生成 PDF 文件。
  • 封装: 使用 Hook 方式封装,方便复用。

二、核心痛点与解决方案

在实现过程中,通常会遇到以下几个问题:

  1. 导出内容不全: 如果页面有滚动条,直接截图只能截取可视区域。
    • 解法: 在截图前将 DOM 高度设置为 auto,并获取 scrollHeight 传递给 html2canvas 的 windowHeight 参数。
  2. 图片模糊: 默认截图出来的 PDF 很模糊。
    • 解法: 设置 scale: 2,提高 Canvas 的像素密度。
  3. PDF 分页问题: 长图直接放入 PDF 会被压缩变形。
    • 解法: 计算内容高度与 A4 纸高度的比例,通过循环 addPage() 实现自动分页切割。

三、源码实现

新建文件 useExportPdf.ts,下载依赖 html2canvas 和 jspdf 然后引入:

import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';

/**
 * 导出页面为 PDF
 * @param dom 需要导出的 DOM 元素
 * @param fileName 导出的文件名(不含后缀)
 */
export const useExportPDF = async (dom: HTMLElement, fileName: string) => {
  const element = dom;
  if (!element) {
    console.error('导出失败,未找到导出元素');
    return;
  }

  // 1. 解决滚动截断问题:获取元素实际高度
  const originalHeight = element.scrollHeight;
  // 临时设置高度为 auto,确保能截取到所有内容
  const originalStyleHeight = element.style.height;
  element.style.height = 'auto';

  try {
    // 2. 将 DOM 转换为 Canvas
    const canvas = await html2canvas(element, {
      useCORS: true, // 允许跨域图片
      scale: 2, // 2 倍缩放,解决模糊问题
      scrollY: -window.scrollY, // 修正滚动条偏移
      scrollX: 0,
      windowHeight: originalHeight, // 告诉 html2canvas 完整高度
    });

    // 3. 初始化 PDF 实例
    // p: 纵向,mm: 单位毫米,a4: 纸张格式
    const pdf = new jsPDF('p', 'mm', 'a4');
    // A4 纸内容宽度(留边距)
    const imgWidth = 190;
    // 根据宽度计算等比例的高度
    const imgHeight = (canvas.height * imgWidth) / canvas.width;
    // 获取 PDF 页面可用高度
    const pdfPageHeight = pdf.internal.pageSize.getHeight();

    // 4. 处理分页逻辑
    let position = 0; // 第一页
    pdf.addImage(canvas, 'PNG', 10, 10 - position, imgWidth, imgHeight);
    position += pdfPageHeight;

    // 如果内容高度超过一页,循环添加新页
    while (position < imgHeight) {
      pdf.addPage();
      // 移动图片位置,实现视觉上的'接续'
      // 注意:这里简单的 position += pageHeight 可能需要根据实际情况调整,
      // 比如减去一些边距来防止文字被切断,Demo 中使用了简化的逻辑。
      pdf.addImage(canvas, 'PNG', 10, 10 - position, imgWidth, imgHeight);
      position += pdfPageHeight;
    }

    // 5. 保存文件
    pdf.save(`${fileName}.pdf`);
  } catch (error) {
    console.error('导出 PDF 异常:', error);
  } finally {
    // 6. 恢复原始样式
    element.style.height = originalStyleHeight;
  }
};

四、如何在组件中使用

在 Vue 组件中,只需要获取到 DOM 引用,然后调用这个 Hook 即可。

<template>
  <a-button type="primary" @click="exportPDF" v-if="disabled">
    导出 PDF
  </a-button>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { useExportPDF } from '/@/hooks/exportpdf/useExportpdf';

// 导出的 DOM 元素
const pdfContainer = ref<HTMLDivElement>(null);
const loading = ref(false);

// 导出
const exportPDF = async () => {
  loading.value = true;
  try {
    await useExportPDF(pdfContainer.value, 'xxxxpdf');
  } catch (e) {
    console.log(e);
  } finally {
    loading.value = false;
  }
};
</script>

五、值得注意的事项

  • 跨域问题: html2canvas 将 DOM 元素转成 Canvas 图片时,如果 DOM 中有图片,需要解决跨域问题。一般可以在服务端(图片源)设置图片的响应头(Response Header),必须包含 CORS 头,允许你的域名访问;或者 Nginx 配置下代理;或者前端解决的话就把图片转成 Base64 格式。但如果图片比较多,不建议前端解决,因为转图片格式消耗时间多,且 Base64 会增加文件大小。
  • 忽略元素: 如果想导致的内容之中,有些是不用导出,或者根据不同条件来区分是否导出,可以使用 data-html2canvas-ignore 属性,设置为 true 就不会导出这个元素。

六、总结

通过这个封装,我们实现了一个轻量级且功能完备的 PDF 导出工具。它不仅解决了最让人头疼的长页面截断问题,还通过 scale 参数保证了导出的清晰度。

七、小思考

为什么 img 标签就能通过图片 URL 加载图片,但是把图片转成 Canvas 就会出现跨域问题?

简单来说就是 img 标签只是'展示'数据,而转成 Canvas 需要'读取'数据。浏览器的安全策略(同源策略)就是'看一眼'和'拿走数据'的区别。

img 标签展示数据跟把图片转成 Canvas 浏览器都会请求图片,服务器返回图片数据。区别在于:

  1. img 标签加载

    • 请求头: 浏览器发起请求时,Origin 字段可能不被包含(或者是 null),或者仅仅作为 Referer 发送。它通常被视为一个'简单请求'。
    • 响应头: 服务器返回图片数据。通常不需要包含 Access-Control-Allow-Origin 等 CORS 相关头信息。
    • 结果: 浏览器接收到数据,渲染引擎直接解码并在屏幕上绘制像素。JavaScript 无法接触到这些数据。
  2. 开启 CORS 的情况(crossorigin="anonymous" 或 Canvas 请求)

    • 请求头: 浏览器强制添加 Origin: [your-domain.com] 字段,明确告诉服务器是谁在请求。
    • 响应头(关键差别):
      • 如果服务器支持跨域:必须返回 Access-Control-Allow-Origin: * 或 Access-Control-Allow-Origin: [your-domain.com]。
      • 如果服务器不支持:服务器可能正常返回了图片数据(状态码 200),但缺少了 CORS 响应头。
    • 结果:
      • 如果有 CORS 头:浏览器认为这份数据是'安全'的,允许 Canvas 读取和导出。
      • 如果没有 CORS 头:虽然数据下载下来了,但浏览器(网络层或渲染层)会拦截这次加载,报错 CORS policy,图片甚至可能直接裂开(加载失败),更别说画到 Canvas 上了。

目录

  1. 封装好用的页面导出 PDF 工具 Hook (html2canvas + jspdf)
  2. 一、技术选型
  3. 二、核心痛点与解决方案
  4. 三、源码实现
  5. 四、如何在组件中使用
  6. 五、值得注意的事项
  7. 六、总结
  8. 七、小思考
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • WhisperX:70 倍实时语音转录、词级时间戳与多说话人分离技术
  • Python 基础语法与核心概念详解
  • AI 生成图片 R18 提示词:高效创作与合规性实践指南
  • C++ STL Stack、Queue、Deque 容器适配器接口详解
  • C++26 反射特性概述及 GCC 14 核心 API 解析
  • 基于YOLOv11与Django的农业植物病害检测系统
  • Qwen2.5-7B-Instruct 并行调用多个 Tools 入门
  • AIGC 检测:GLM-4.6V-Flash-WEB 如何辨别 AI 生成图像?
  • Spring Boot 电影院后台管理系统实战指南
  • CSS 元素显示模式详解:块级、行内与行内块
  • Spring Web MVC 核心原理与实战开发
  • 使用 Higress 将 REST API 转换为 MCP Server 工具
  • Struts2 接收请求参数:基本类型与复合类型
  • 深入解析程序翻译环境:从源代码到可执行文件
  • Android Framework 核心原理与源码解析指南
  • Trae 配置 C++ 编译环境实战指南
  • 二叉树深度计算与先序排列求解实战
  • AIGC 从创意到创造:核心概念与落地场景
  • 企业应用探秘:大模型 AI Agent 的六种基础类型
  • 算法实战:寻找数组中心下标与除自身外数组乘积(前缀和技巧)

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online