react-pdf/renderer IE 需要很长时间才能生成 blob 以下载 pdf

react-pdf/renderer IE takes very long time to generate blob to download pdf

    async function saveBlob() {
        const doc = <TableDocument/>;
        const asPdf = pdf([]);
        await asPdf.updateContainer(doc);
        const blob = await asPdf.toBlob();
        saveAs(blob, 'instructions.pdf');
    }

    return (
        <IconButton
            onClick={() => saveBlob()}>
        </IconButton>
    );

这就是我尝试通过获取 blob 然后下载它来生成我的 pdf 文档的方式。我只使用 reactJS 在客户端做所有事情。

在 Chrome 和 Edge 浏览器上只需要 3-4 秒,但在 IE 上大约需要 30-40 秒。它是一个 3 页的 pdf 文档,带有简单的 table(对于单页 IE 大约需要 8-9 秒)。

甚至在使用 async await 之后它也会阻塞 UI。 任何有助于减少在 IE 上生成 pdf 的时间的帮助将不胜感激。

我找到了 pdfmake(https://pdfmake.github.io/docs/0.1/) 库,它从客户端生成 pdf 的速度非常快。在 IE 上也只需 2 秒即可生成 pdf。

function TablePDF(t, headerGroups, rows, pageName, messageTitle, messageBody, fontLocale, fontMargin, rowFlag) {
  const moreRows = [...rows];

  const createTableHeader = col => {
    const val = t(col.Header);
    return { text: val && val.replace(/<\/br>|<br>/g, '\n'), style: 'tableHeader' };
  };

  const createTableRow = row => {
    const rowElement = [];
    Object.entries(row).map(([key, val]) => {
      if (key === 'notes' || (rowFlag && key === 'fileNameFileReference')) return null;
      const rowCellFormatted =
        key === 'instructionStatus'
          ? mapInstructionStatus(val)
          : key === 'instructionType'
          ? t(mapInstructionTypeStoreData(val))
          : val;

      const cellText =
        (rowCellFormatted &&
          (typeof rowCellFormatted === 'string' && rowCellFormatted.replace(/<\/br>|<br>/g, '\n'))) ||
        rowCellFormatted ||
        '';
      rowElement.push({
        text: cellText,
        margin: [5, fontMargin(getLanguageFont(cellText)), 5, 5],
        font: getLanguageFont(cellText)
      });
      return null;
    });
    return rowElement;
  };

  const getTableHeader = () => {
    const arrOfHeader =
      headerGroups &&
      headerGroups.length &&
      headerGroups[0].headers
        .filter(col => {
          if (
            col.render('Header') === 'ila.notes' ||
            (rowFlag && col.render('Header') === 'ila.fileNameFileReference')
          ) {
            return false;
          }
          return true;
        })
        .map((col, index) => createTableHeader(col, index));
    return arrOfHeader;
  };

  const arrOfHeader = getTableHeader();

  const getTableRows = () => moreRows.map(row => createTableRow(row.values));

  const pdfNote = t('ila.pdfNote');

  const mapInstructionStatus = status => {
    let statusToReturn = '';
    const INSTRUCTION_STATUS = ROW_INSTRUCTION_STATUS;
    switch (status) {
      case 'PA':
        statusToReturn = INSTRUCTION_STATUS[0].label;
        break;
      case 'RA':
        statusToReturn = INSTRUCTION_STATUS[1].label;
        break;
      case 'P2':
        statusToReturn = INSTRUCTION_STATUS[2].label;
        break;
      case 'R2':
        statusToReturn = INSTRUCTION_STATUS[3].label;
        break;
      case 'P3':
        statusToReturn = INSTRUCTION_STATUS[4].label;
        break;
      case 'R3':
        statusToReturn = INSTRUCTION_STATUS[5].label;
        break;
      case 'P4':
        statusToReturn = INSTRUCTION_STATUS[6].label;
        break;
      case 'P5':
        statusToReturn = INSTRUCTION_STATUS[7].label;
        break;
      case 'RB':
        statusToReturn = 'ila.receivedByBank';
        break;
      case 'JB':
        statusToReturn = 'ila.rejectedByBank';
        break;
      case 'JC':
        statusToReturn = 'ila.rejectedByCustomer';
        break;
      case 'RF':
        statusToReturn = 'ila.receivedFwdInstr';
        break;
      default:
        statusToReturn = t(`ila.instructionStatus_${status}`) ? `ila.instructionStatus_${status}` : '';
        break;
    }
    return t(statusToReturn);
  };

  const mapInstructionTypeStoreData = type => {
    switch (type) {
      case 'C0':
      case 'C1':
      case 'PP':
        return 'ila.pp';
      case 'CS':
      case 'COS':
        return 'ila.cos';
      case 'AP':
      case 'ACH':
      case 'APC':
        return 'ila.achCredit';
      case 'AD':
      case 'APD':
        return 'ila.achDebit';
      default:
        return type;
    }
  };

  const tableDoc = {
    pageOrientation: 'landscape',
    pageMargins: [25, 74, 25, 60],
    footer: (currentPage, pageCount) => PDFFooter(pageName, pageCount, currentPage),
    header: () => PDFHeader(fontMargin(fontLocale), pageName),
    content: [
      {
        text: [{ text: messageTitle, bold: true, color: 'black' }, { text: messageBody }, '\n', { text: pdfNote }],
        style: 'note'
      },
      {
        style: 'tableStyle',
        table: {
          headerRows: 1,
          dontBreakRows: true,
          body: [arrOfHeader, ...getTableRows()],
          widths:
            arrOfHeader.length === 9
              ? ['13.3%', '11.3%', '11.3%', '13.3%', '12.3%', '9.1%', '9.1%', '8.6%', '11.6%']
              : ['12.8%', '12.8%', '14.8%', '13.8%', '10.6%', '10.6%', '11.4%', '13.1%']
        },
        layout: {
          vLineWidth: () => 1.2,
          hLineWidth: hLineIndex => (hLineIndex === 1 ? 0 : 1.2),
          hLineColor: () => '#E4E8EA',
          // eslint-disable-next-line no-confusing-arrow
          vLineColor: (i, node, rowIndex) =>
            rowIndex === 0 || (i === 0 || i === arrOfHeader.length) ? 'white' : '#E4E8EA',
          fillColor: rowIndex => (rowIndex === 0 ? '#E4E8EA' : null)
        }
      }
    ],
    styles: {
      tableStyle: {
        margin: [0, fontMargin(fontLocale), 0, 20]
      },
      tableHeader: {
        bold: true,
        color: 'black',
        margin: [5, fontMargin(fontLocale), 5, 5]
      },
      note: {
        lineHeight: 2,
        italics: false
      }
    },
    defaultStyle: {
      columnGap: 10,
      font: fontLocale,
      color: '#333333',
      fontSize: 8
    }
  };
  return tableDoc;
}
export default TablePDF;

const date = new Date();
const gmtString = date.toGMTString();

const finalDate =
  JSON.stringify(gmtString).slice(6, 17) +
  ',' +
  JSON.stringify(gmtString).slice(17, 23) +
  JSON.stringify(gmtString).slice(26, 30);

const PDF_Footer = (pageName, totalPages, pageNumber, reference) => ({
  margin: [30, 20, 30, 10],
  stack: [
    {
      table: {
        headerRows: 1,
        widths: ['*'],
        body: [[''], ['']]
      },
      layout: {
        hLineWidth: i => (i === 0 ? 1 : 0),
        vLineWidth: () => 0,
        hLineColor: () => 'black'
      }
    },
    {
      columns: [
        finalDate + ' | ' + pageName + (reference || ''),
        {
          text: pageNumber && totalPages && `Page ${pageNumber} of ${totalPages}`,
          alignment: 'right'
        }
      ]
    }
  ],
  color: '#333333',
  bold: false
});

export default PDF_Footer;



export function getLanguageFont(text) {
  const universal = /^[a-zA-Z0-9_\-/.!@#$%^&*' 'wığüşöçĞÜŞÖÇİÀ-ÿ/]+$/;
  const arabic = /[\u0600-\u06FF]/;
  const hebrew = /[\u0590-\u05FF]/;
  const greek = /[\u0370-\u03ff\u1f00-\u1fff]/;
  const japanese = /[\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\uff00-\uff9f\u4e00-\u9faf\u3400-\u4dbf]/;
  const korean = /[\u3130-\u318F\uAC00-\uD7AF]/g;
  const russian = /[\u0401\u0451\u0410-\u044f]/;
  const vietnamese = /^[a-zA-ZÀÁÂÃÈÉÊÌÍÒÓÔÕÙÚĂĐĨŨƠàáâãèéêìíòóôõùúăđĩũơƯĂẠẢẤẦẨẪẬẮẰẲẴẶẸẺẼỀỀỂẾưăạảấầẩẫậắằẳẵặẹẻẽềềểếỄỆỈỊỌỎỐỒỔỖỘỚỜỞỠỢỤỦỨỪễệỉịọỏốồổỗộớờởỡợụủứừỬỮỰỲỴÝỶỸửữựỳỵỷỹ\s\W|_]+$/;
  const chinese = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f]/;
  if (universal.test(text)) {
    return 'UniverseNextHSBC';
  } else if (arabic.test(text)) {
    return 'arabicFont';
  } else if (hebrew.test(text)) {
    return 'hebrewFont';
  } else if (greek.test(text)) {
    return 'hebrewFont';
  } else if (japanese.test(text)) {
    return 'chineseSimple';
  } else if (korean.test(text)) {
    return 'korean';
  } else if (vietnamese.test(text)) {
    return 'vietnamese';
  } else if (chinese.test(text)) {
    return 'chineseSimple';
  }

  return 'UniverseNextHSBC';
}

      pdfMake.createPdf(tableDoc, null, fontCategories).download(('file.pdf', () => setDownloading(false)));