在网页开发中,实现 PDF 下载功能是常见的需求。以下是几种主流实现方式及其详细代码示例: 
方案一,使用浏览器原生API(window.print)
<!DOCTYPE html><html><head>    <title>打印为PDF</title>    <style>        @media print {            .no-print {                display: none;            }            body {                margin: 0;                padding: 10mm;            }        }    </style></head><body>    <div id="printable-content">        <h1>可打印内容</h1>        <p>使用浏览器打印功能保存为PDF</p>    </div>
    <button class="no-print" onclick="window.print()">打印/保存为PDF</button></body></html>
这个插件仅仅是唤起打印的功能,让用户另存为 pdf 不合适
方案二,使用纯前端方案(jsPDF + html2canvas)
<!DOCTYPE html><html><head>    <title>HTML转PDF</title>    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>    <style>        #content {            width: 800px;            padding: 20px;            background: #f5f5f5;        }    </style></head><body>    <div id="content">        <h1>这是要导出为PDF的内容</h1>        <p>使用jsPDF和html2canvas库可以轻松实现HTML转PDF功能</p>        <table border="1">            <tr><th>姓名</th><th>年龄</th></tr>            <tr><td>张三</td><td>25</td></tr>        </table>    </div>
    <button onclick="generatePDF()">下载PDF</button>
    <script>        function generatePDF() {            const { jsPDF } = window.jspdf;            const element = document.getElementById('content');
            html2canvas(element).then(canvas => {                const imgData = canvas.toDataURL('image/png');                const pdf = new jsPDF('p', 'mm', 'a4');                const imgProps = pdf.getImageProperties(imgData);                const pdfWidth = pdf.internal.pageSize.getWidth();                const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
                pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight);                pdf.save('document.pdf');            });        }    </script></body></html>
看着几乎完美,这个技术栈,最核心的就是:必须要用到 dom 元素渲染,试想一下,你做了一个导出功能,总不能让客户必须先打开页面等 html 渲染完后,再导出吧?或者display:none,打印出来一个空白。
此路不通,就只能重新寻找新的方向
方案三,html2pdf
npm install html2pdf.js
<template>  <div class="container">    <button @click="generatePDF">下载PDF</button>  </div></template><script setup>import html2pdf from 'html2pdf.js'let element = `    <h1>前端人</h1>    <p>学好前端,走遍天下都不怕</p>    ...`;function generatePDF() {        const opt = {      margin:       10,      filename:     'hello_world.pdf',      image:        { type: 'jpeg', quality: 0.98 },      html2canvas:  { scale: 2 },      jsPDF:        { unit: 'mm', format: 'a4', orientation: 'portrait' }    };        html2pdf().from(element).set(opt).save();}</script>
功能正常,似乎一切都完美
问题没有想的那么简单如果我们的html是纯文本元素,这程序跑起来没有任何问题,但我们抓取的信息都源于互联网,html结构怎么可能会这么简单?如果我们的html中包含图片信息 ,此时你会发现,导出来的 pdf,图片占位处是个空白块
那我理解的图片同步加载是什么意思呢?简单来说,就是将图片转成Base64,因为这种方式,即使说无网的情况也能正常加载图片,因此我凭感觉断定,这就是图片同步加载
基于这个思路,我写了个完整 demo
<template>  <div class="container">    <button @click="generatePDF">下载PDF</button>  </div></template><script setup>import html2pdf from 'html2pdf.js'async function convertImagesToBase64(htmlString) {    const tempDiv = document.createElement('div');  tempDiv.innerHTML = htmlString;    const images = tempDiv.querySelectorAll('img');    for (const img of images) {    try {      const base64 = await getBase64FromUrl(img.src);      img.src = base64;    } catch (error) {      console.error(`无法转换图片 ${img.src}:`, error);          }  }    return tempDiv.innerHTML;}function getBase64FromUrl(url) {  return new Promise((resolve, reject) => {    const img = new Image();    img.crossOrigin = 'Anonymous';     img.onload = () => {      const canvas = document.createElement('canvas');      canvas.width = img.width;      canvas.height = img.height;      const ctx = canvas.getContext('2d');      ctx.drawImage(img, 0, 0);            const dataURL = canvas.toDataURL('image/png');      resolve(dataURL);    };    img.onerror = () => {      reject(new Error('图片加载失败'));    };    img.src = url;  });}let element = `  <div>    <img src='http://t13.baidu.com/it/u=2041049195,1001882902&fm=224&app=112&f=JPEG?w=500&h=500' style="width: 300px;" />    <p>职业:前端</p>    <p>技能:唱、跳、rap</p>  </div>`;function generatePDF() {  element =`<style>  img {      max-width: 100%;      max-height: 100%;      vertical-align: middle;      height: auto !important;      width: auto !important;      margin: 10px 0;    }  </style>` + element;  convertImagesToBase64(element)  .then(convertedHtml => {        const opt = {      margin:       10,      filename:     '前端大法好.pdf',      image:        { type: 'jpeg', quality: 0.98 },      html2canvas:  { scale: 2 },      jsPDF:        { unit: 'mm', format: 'a4', orientation: 'portrait' }    };        html2pdf().from(convertedHtml).set(opt).save();  })  .catch(error => {    console.error('转换过程中出错:', error);  });}</script>
此时就大功告成啦!不过得提一句:图片的 URL 链接必须是同源或者允许跨越的,否则就会存在图片加载异常的问题。
阅读原文:原文链接
该文章在 2025/7/3 14:24:50 编辑过