前端實(shí)現(xiàn)HTML轉(zhuǎn)PDF的兩種主流方案詳解
目前前端生成 PDF 最主流的兩種方案是:
- 客戶(hù)端方案:html2canvas + jsPDF(或其封裝庫(kù) html2pdf.js)
- 服務(wù)端方案:
Puppeteer / Playwright(Node.js 無(wú)頭瀏覽器)
這兩種方案幾乎占據(jù)了 90% 以上的實(shí)際項(xiàng)目。下面從原理、優(yōu)缺點(diǎn)、適用場(chǎng)景、核心代碼、坑點(diǎn)與優(yōu)化等維度進(jìn)行深度對(duì)比。
一、核心對(duì)比表(快速?zèng)Q策)
| 維度 | 客戶(hù)端方案(html2canvas + jsPDF) | 服務(wù)端方案(Puppeteer / Playwright) | 勝出方 |
|---|---|---|---|
| 實(shí)現(xiàn)難度 | ★☆☆☆☆(最簡(jiǎn)單) | ★★★☆☆(需后端) | 客戶(hù)端 |
| 生成質(zhì)量 | 中等(樣式丟失常見(jiàn)) | 極高(接近瀏覽器打印效果) | 服務(wù)端 |
| 中文/字體支持 | 較差(需特殊處理) | 優(yōu)秀(可加載本地字體) | 服務(wù)端 |
| 大文件 / 長(zhǎng)頁(yè)面 | 容易卡頓、崩潰 | 穩(wěn)定 | 服務(wù)端 |
| 分頁(yè)控制 | 弱(需 hack) | 強(qiáng)(支持 @page、頁(yè)眉頁(yè)腳) | 服務(wù)端 |
| 部署復(fù)雜度 | 零(純前端) | 中等(需 Node 服務(wù)) | 客戶(hù)端 |
| 性能壓力 | 前端瀏覽器承擔(dān) | 后端服務(wù)器承擔(dān) | 看場(chǎng)景 |
| 安全性 | 高(客戶(hù)端) | 中(后端需注意 HTML 注入) | 客戶(hù)端 |
| 2026 年推薦場(chǎng)景 | 簡(jiǎn)單報(bào)表、導(dǎo)出預(yù)覽、H5 小程序 | 正式合同、發(fā)票、復(fù)雜報(bào)告、打印級(jí) PDF | — |
二、方案一:客戶(hù)端 —— html2canvas + jsPDF(最常用)
原理
html2canvas把 DOM 元素渲染成 Canvas(像素級(jí)截圖)jsPDF把 Canvas 轉(zhuǎn)為 PDF 文件并下載
推薦庫(kù)
- 直接用
html2pdf.js(封裝版,最推薦) - 或手動(dòng)組合
html2canvas + jsPDF
核心代碼(Vue 3 + html2pdf.js)
<template>
<div ref="content">
<!-- 你的 HTML 內(nèi)容 -->
<h1>發(fā)票標(biāo)題</h1>
<table>...</table>
</div>
<button @click="exportPDF">導(dǎo)出 PDF</button>
</template>
<script setup>
import html2pdf from 'html2pdf.js'
const content = ref(null)
const exportPDF = () => {
const opt = {
margin: [10, 10, 10, 10],
filename: 'report.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: {
scale: 2, // 清晰度(2倍最合適)
useCORS: true, // 跨域圖片
letterRendering: true
},
jsPDF: {
unit: 'mm',
format: 'a4',
orientation: 'portrait'
}
}
html2pdf().set(opt).from(content.value).save()
}
</script>
優(yōu)點(diǎn)
- 零后端,部署最簡(jiǎn)單
- 適合 H5、小程序、內(nèi)部工具
致命缺點(diǎn)(2026 年仍未徹底解決)
- 復(fù)雜 CSS3(flex、grid、陰影、漸變)容易變形
- 中文字體模糊或缺失
- 超長(zhǎng)頁(yè)面容易內(nèi)存溢出
- 分頁(yè)控制極差(經(jīng)常出現(xiàn)表格斷行、頁(yè)眉頁(yè)腳難處理)
優(yōu)化技巧:
- 使用
scale: 2提升清晰度 - 提前把需要導(dǎo)出的內(nèi)容克隆到一個(gè)隱藏的 div 中,專(zhuān)門(mén)優(yōu)化樣式
- 對(duì)于表格推薦使用
jspdf-autotable插件單獨(dú)處理
三、方案二:服務(wù)端 —— Puppeteer / Playwright(質(zhì)量之王)
原理
啟動(dòng)無(wú)頭瀏覽器(Headless Chrome / Firefox / WebKit),加載完整 HTML 頁(yè)面后,直接調(diào)用 page.pdf() 生成 PDF。
2026 年推薦:Playwright(已全面超越 Puppeteer)
Playwright 優(yōu)勢(shì):多瀏覽器支持、自動(dòng)等待、API 更現(xiàn)代、穩(wěn)定性更高。
核心代碼(Node.js + Express)
// pdfService.js
const { chromium } = require('playwright')
async function htmlToPdf(htmlContent) {
const browser = await chromium.launch({ headless: true })
const page = await browser.newPage()
// 關(guān)鍵:設(shè)置視口和打印樣式
await page.setContent(htmlContent, { waitUntil: 'networkidle' })
const pdfBuffer = await page.pdf({
format: 'A4',
printBackground: true,
margin: { top: '15mm', bottom: '15mm', left: '10mm', right: '10mm' },
displayHeaderFooter: true,
headerTemplate: '<div style="font-size:10px;width:100%;text-align:center;">頁(yè)眉</div>',
footerTemplate: '<div style="font-size:10px;width:100%;text-align:center;">第 <span class="pageNumber"></span> 頁(yè)</div>'
})
await browser.close()
return pdfBuffer
}
前端調(diào)用示例
const res = await fetch('/api/export-pdf', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ html: document.getElementById('content').outerHTML })
})
const blob = await res.blob()
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'report.pdf'
a.click()
優(yōu)點(diǎn)
- 生成效果幾乎和瀏覽器打印一致
- 原生支持
@page、page-break-before/after分頁(yè)控制 - 中文字體完美(可加載本地字體或系統(tǒng)字體)
- 支持頁(yè)眉頁(yè)腳、加密、水印、目錄等高級(jí)功能
缺點(diǎn)
- 需要部署 Node 服務(wù)
- 并發(fā)高時(shí)消耗服務(wù)器資源(可使用 puppeteer-cluster / playwright-cluster 做池化)
四、最終推薦(2026 年真實(shí)選擇)
- 推薦客戶(hù)端方案:內(nèi)部工具、簡(jiǎn)單報(bào)表、H5 活動(dòng)頁(yè)、快速 MVP
- 強(qiáng)烈推薦服務(wù)端方案:合同、發(fā)票、正式報(bào)告、需要精確分頁(yè)和美觀樣式的場(chǎng)景
- 混合方案(最優(yōu)解):前端負(fù)責(zé)編輯預(yù)覽 + 服務(wù)端負(fù)責(zé)最終生成 PDF(目前大廠(chǎng)主流做法)
你現(xiàn)在要做的是哪類(lèi) PDF?
- 簡(jiǎn)單數(shù)據(jù)報(bào)表?
- 合同/發(fā)票類(lèi)正式文件?
- 還是需要頁(yè)眉頁(yè)腳 + 精確分頁(yè)的復(fù)雜報(bào)告?
告訴我具體需求,我可以給你對(duì)應(yīng)方案的最優(yōu)完整代碼模板。
以上就是前端實(shí)現(xiàn)HTML轉(zhuǎn)PDF的兩種主流方案詳解的詳細(xì)內(nèi)容,更多關(guān)于前端HTML轉(zhuǎn)PDF方案的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
checkbox全選所涉及到的知識(shí)點(diǎn)介紹
checkbox全選涉及到的知識(shí)點(diǎn)比如IE里起作用,火狐不起作用,getElementById()與getElementsByName()的區(qū)別等等2013-12-12
編寫(xiě)更好的JavaScript條件式和匹配條件的技巧(小結(jié))
這篇文章主要介紹了編寫(xiě)更好的JavaScript條件式和匹配條件的技巧(小結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
詳解javaScript中Number數(shù)字類(lèi)型的使用
Number和Math都屬于JavaScript中的內(nèi)置對(duì)象,Number數(shù)字類(lèi)型作為基礎(chǔ)數(shù)據(jù)類(lèi)型,我們?cè)陂_(kāi)發(fā)過(guò)程中會(huì)經(jīng)常用到,包括數(shù)字精度的格式化,還有字符串轉(zhuǎn)換成數(shù)字等操作。本文將詳細(xì)講解其用法,感興趣的可以了解一下2022-04-04
JavaScript實(shí)現(xiàn)滑塊補(bǔ)圖驗(yàn)證碼效果
這篇文章主要給大家介紹了JavaScript如何實(shí)現(xiàn)滑塊補(bǔ)圖驗(yàn)證碼效果,文章通過(guò)代碼示例介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以參考閱讀下2023-07-07
使用JavaScript實(shí)現(xiàn)ajax的實(shí)例代碼
實(shí)現(xiàn)ajax之前必須要?jiǎng)?chuàng)建一個(gè) XMLHttpRequest 對(duì)象。這是必須的。那么對(duì)使用js實(shí)現(xiàn)ajax的代碼感興趣的朋友可以參考下本文2016-05-05
element?table?點(diǎn)擊某一行中按鈕加載功能實(shí)現(xiàn)
在Element UI中,實(shí)現(xiàn)表格(element-table)中的這種功能通常涉及到數(shù)據(jù)處理和狀態(tài)管理,這篇文章主要介紹了element?table?點(diǎn)擊某一行中按鈕加載功能,需要的朋友可以參考下2024-06-06
javascript實(shí)現(xiàn)原生ajax的幾種方法介紹
項(xiàng)目中不需要加載jquery這種龐大的js插件要使用到ajax這種功能該如何辦呢?下面和大家分享幾種利用javascript實(shí)現(xiàn)原生ajax的方法2013-09-09
JS動(dòng)態(tài)給對(duì)象添加屬性和值的實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇JS動(dòng)態(tài)給對(duì)象添加屬性和值的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10

