前端导出大量数据到PDF方案

前端导出大量数据到PDF的方案:

1. 纯前端方案

jsPDF + autoTable(最常用)

javascript 复制代码
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');
}

分页处理大量数据

javascript 复制代码
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. 性能优化方案

虚拟滚动 + 分批处理

javascript 复制代码
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. 服务端辅助方案

前端生成 + 服务端合并

javascript 复制代码
// 前端:分批生成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

javascript 复制代码
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环境)

javascript 复制代码
// 适用于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. 最佳实践建议

内存管理

javascript 复制代码
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();
}

进度反馈

javascript 复制代码
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 + 分批处理

实际项目中,建议根据数据量大小和性能要求选择合适的方案,并始终提供进度反馈以改善用户体验。

相关推荐
maogewang5 分钟前
清朝条约全集 PDF 电子版(三册合集):从尼布楚到辛丑条约的完整史料集
pdf
柒.梧.13 分钟前
HTML入门指南:30分钟掌握网页基础
前端·javascript·html
用户542778485154015 分钟前
Promise :从基础原理到高级实践
前端
用户40993225021218 分钟前
Vue3条件渲染中v-if系列指令如何合理使用与规避错误?
前端·ai编程·trae
Mr_Swilder21 分钟前
2025-12-20 vue3中 eslint9+和prettier配置
前端
code_YuJun23 分钟前
脚手架开发工具——判断文件是否存在 path-exists
前端
code_YuJun23 分钟前
脚手架开发工具——root-check
前端
用户542778485154024 分钟前
XMLHttpRequest、AJAX、Fetch 与 Axios
前端
打小就很皮...31 分钟前
React 实现富文本(使用篇&Next.js)
前端·react.js·富文本·next.js
智算菩萨1 小时前
实战:高级中文自然语言处理系统的Python设计与实现
前端·javascript·easyui