前端导出大量数据到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 + 分批处理

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

相关推荐
Lj2_jOker6 小时前
QT 给Qimage数据赋值,显示异常,像素对齐的坑
开发语言·前端·qt
csj507 小时前
前端基础之《React(7)—webpack简介-ESLint集成》
前端·react
咚咚咚小柒7 小时前
【前端】Webpack相关(长期更新)
前端·javascript·webpack·前端框架·node.js·vue·scss
2501_916008897 小时前
前端工具全景实战指南,从开发到调试的效率闭环
android·前端·小程序·https·uni-app·iphone·webview
诸葛韩信7 小时前
Webpack与Vite的常用配置及主要差异分析
前端·webpack·node.js
IT_陈寒7 小时前
Vite 5震撼发布!10个新特性让你的开发效率飙升200% 🚀
前端·人工智能·后端
一路向前的月光7 小时前
uniapp(5)滚动列表scroll-view
前端·javascript·uni-app
Hilaku7 小时前
就因为package.json里少了个^号,我们公司赔了客户十万块
前端·javascript·npm
晴殇i7 小时前
尤雨溪创立的 VoidZero 完成 1250 万美元 A 轮融资,加速整合前端工具链生态
前端·vue.js