前端导出大量数据到PDF的方案:
1. 纯前端方案
jsPDF + autoTable(最常用)
import jsPDF from 'jspdf';
import 'jspdf-autotable';
function exportToPDF(data) {
const doc = new jsPDF();
// 设置表格列
const columns = ['ID', '姓名', '邮箱', '部门'];
// 转换数据
const rows = data.map(item => [
item.id,
item.name,
item.email,
item.department
]);
// 生成表格
doc.autoTable({
head: [columns],
body: rows,
styles: { fontSize: 8 },
margin: { top: 10 }
});
doc.save('数据导出.pdf');
}
分页处理大量数据
function exportLargeDataToPDF(data, chunkSize = 500) {
const doc = new jsPDF();
const columns = ['ID', '姓名', '邮箱'];
for (let i = 0; i < data.length; i += chunkSize) {
if (i !== 0) {
doc.addPage(); // 添加新页
}
const chunk = data.slice(i, i + chunkSize);
const rows = chunk.map(item => [item.id, item.name, item.email]);
doc.autoTable({
head: [columns],
body: rows,
startY: 20,
styles: { fontSize: 7 },
pageBreak: 'auto'
});
}
doc.save('大数据导出.pdf');
}
2. 性能优化方案
虚拟滚动 + 分批处理
async function exportHugeData(data, batchSize = 1000) {
const doc = new jsPDF();
let currentPage = 1;
for (let i = 0; i < data.length; i += batchSize) {
const batch = data.slice(i, i + batchSize);
// 显示进度
updateProgress(i, data.length);
if (i > 0) {
doc.addPage();
currentPage++;
}
// 使用Web Worker处理数据转换
const rows = await processBatchInWorker(batch);
doc.autoTable({
head: [['ID', '姓名', '邮箱']],
body: rows,
startY: 20,
styles: { fontSize: 6 },
margin: { left: 5, right: 5 }
});
// 让出主线程避免阻塞
await new Promise(resolve => setTimeout(resolve, 0));
}
doc.save('超大数据导出.pdf');
}
3. 服务端辅助方案
前端生成 + 服务端合并
// 前端:分批生成PDF片段
async function generatePDFChunks(data, chunkSize = 2000) {
const chunks = [];
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
const pdfBlob = await generateChunkPDF(chunk);
chunks.push(pdfBlob);
}
// 发送到服务端合并
const finalPDF = await mergePDFsOnServer(chunks);
downloadBlob(finalPDF, '合并报表.pdf');
}
4. 基于Canvas的方案
html2canvas + jsPDF
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
async function exportDivToPDF(elementId, filename = '导出.pdf') {
const element = document.getElementById(elementId);
const canvas = await html2canvas(element, {
scale: 2,
useCORS: true,
logging: false
});
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({
orientation: 'portrait',
unit: 'mm',
format: '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(filename);
}
5. 流式处理方案
使用PDFKit(Node.js环境)
// 适用于Electron或Node.js环境
const PDFDocument = require('pdfkit');
const fs = require('fs');
function createStreamingPDF(data, outputPath) {
const doc = new PDFDocument({ autoFirstPage: false });
doc.pipe(fs.createWriteStream(outputPath));
let rowCount = 0;
const rowsPerPage = 40;
data.forEach((item, index) => {
if (rowCount % rowsPerPage === 0) {
if (index > 0) doc.addPage();
addTableHeader(doc);
}
addTableRow(doc, item, rowCount % rowsPerPage);
rowCount++;
});
doc.end();
}
6. 最佳实践建议
内存管理
function optimizedExport(data) {
// 1. 数据分片
const chunks = chunkArray(data, 1000);
// 2. 清理不需要的引用
let currentChunkIndex = 0;
function processNextChunk() {
if (currentChunkIndex >= chunks.length) return;
const chunk = chunks[currentChunkIndex];
processChunk(chunk);
// 释放内存
chunks[currentChunkIndex] = null;
currentChunkIndex++;
// 非阻塞处理
setTimeout(processNextChunk, 100);
}
processNextChunk();
}
进度反馈
function exportWithProgress(data) {
const total = data.length;
let processed = 0;
showProgressBar();
const interval = setInterval(() => {
updateProgressBar((processed / total) * 100);
if (processed >= total) {
clearInterval(interval);
showCompletionMessage();
}
}, 100);
}
7. 推荐方案选择
- 中小数据量(< 10,000行):jsPDF + autoTable
- 大数据量(10,000-100,000行):分页处理 + 进度反馈
- 超大数据量(> 100,000行):服务端生成或流式处理
- 复杂排版需求:html2canvas + jsPDF
- 最高性能要求:Web Worker + 分批处理
实际项目中,建议根据数据量大小和性能要求选择合适的方案,并始终提供进度反馈以改善用户体验。
477

被折叠的 条评论
为什么被折叠?



