react-pdf(pdfjs-dist)如何兼容老浏览器(chrome 49)

之前都是使用react-pdf来渲染pdf文件,这次有个需求是要兼容xp环境,xp上chrome最高支持到49,虽然说iframe或者embed都可以实现预览pdf,但为了后续的定制化需求,还是需要使用js库来渲染。

chrome 49测试环境

能用的测试环境是关键,这里使用chrome 49,是为了兼容xp。

我使用vmware安装了windows10虚拟机,再安装chrome 49来模拟。

pdf.js

一开始我觉得既然react-pdf不能用,那我们就找它封装的原始库pdf.js。

首先参考利用pdf.js在线展示PDF文档 - 老码识途呀 - 博客园,找到能用的pdf.js版本(pdfjs-2.5.207-es5-dist),重点是要支持es5。

使用umi搭建测试Demo

3.1. umi4

第一次我用的umi4搭建的,主要代码如下:

json 复制代码
//package.json
"dependencies": {
  "pdfjs-dist": "2.5.207",
  "umi": "^4.4.11"
},
typescript 复制代码
//index.tsx
import { useEffect, useRef } from "react";
import * as pdfjs from 'pdfjs-dist';
//重点是要用es5
import pdfjsWorker from 'pdfjs-dist/es5/build/pdf.worker.entry.js';

pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;

const pdfUrl = 'http://xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx.pdf';

const PdfTest = () => {
  const pdfContainer = useRef();

  useEffect(() => {
    // Loading a document.
    var loadingTask = pdfjs.getDocument(pdfUrl);
    loadingTask.promise
      .then(function (pdfDocument) {
        // Request a first page
        return pdfDocument.getPage(1).then(function (pdfPage) {
          // Display page on the existing canvas with 100% scale.
          var viewport = pdfPage.getViewport({ scale: 1.0 });
          var canvas = document.getElementById("theCanvas");
          canvas.width = viewport.width;
          canvas.height = viewport.height;
          var ctx = canvas.getContext("2d");
          var renderTask = pdfPage.render({
            canvasContext: ctx,
            viewport: viewport,
          });
          return renderTask.promise;
        });
      })
      .catch(function (reason) {
        console.error("Error: " + reason);
      });

  }, [])

  return (<canvas
    id="theCanvas"
    ref={pdfContainer}
    />);
}

export default PdfTest;

另外umi4打包时还支持添加兼容性配置:

typescript 复制代码
//.umirc.ts
export default defineConfig({
  targets: {
    chrome: 49,
  },
  legacy: {},
});

实测是可以访问的。

3.2. umi2 + react-pdf

由于项目比较老,还在用umi2,不支持legacy配置,光使用umi4是不够的。

另外既然pdf.js可以,理论上react-pdf也是支持的,只要找到对应的版本号。我找到的是react-pdf@5.2.0。

javascript 复制代码
//reactPdf.js
import React, { useState } from 'react';
import { Document, Page, pdfjs } from 'react-pdf';
import pdfjsWorker from 'pdfjs-dist/es5/build/pdf.worker.entry.js';

pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;

const pdfUrl = 'http://xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx.pdf';

function ReactPdf() {
  const [numPages, setNumPages] = useState(null);
  const [pageNumber, setPageNumber] = useState(1);

  function onDocumentLoadSuccess({ numPages }) {
    setNumPages(numPages);
  }

  return (
    <div>
      <Document file={pdfUrl} onLoadSuccess={onDocumentLoadSuccess}>
        <Page pageNumber={pageNumber} />
      </Document>
      <p>
        Page {pageNumber} of {numPages}
      </p>
    </div>
  );
}

export default ReactPdf;
javascript 复制代码
//config.js
export default {
  targets: { //配置浏览器最低版本,比如兼容ie11
    chrome: 49, ie: 9
  },
}

然而实测报错:

错误排查

这里有一个很重要的点,那就是如何找到出错的代码

umi2是支持不压缩代码的:

所以我修改了打包命令:

json 复制代码
//package.json
"build": "cross-env COMPRESS=none umi build",

重新发布后可以看到出错的地方:

定位到源码:

这里可以明显看到出错的原因是不支持async,也就是es6的功能。

报错的这段代码其实出自pdf.js:

javascript 复制代码
//pdf.js@2.5.207/src/display_utils.js
async fetch({ name }) {
  if (!this.baseUrl) {
    throw new Error(
      'The CMap "baseUrl" parameter must be specified, ensure that ' +
      'the "cMapUrl" and "cMapPacked" API parameters are provided.'
    );
  }
  if (!name) {
    throw new Error("CMap name must be specified.");
  }

所以我们的目标是打包时把pdf.js的源码转换为es5

我试了很多方案,比如修改react-pdf源码,把所有pdfjs-dist的引入改成es5:

javascript 复制代码
//react-pdf@5.2.0/src/Document.jsx

// import * as pdfjs from 'pdfjs-dist';
import * as pdfjs from 'pdfjs-dist/es5/build/pdf';

改完以后重新打包并替换node_modules下的文件夹:

实测仍然报错:

另外在本地开发时定位到:

打断点自动跳转到源码:

说明就算我把react-pdf中的所有引入都改成了es5,pdfjs-dist还是会有一些工具类会使用es6的方法。

实际上我们需要的是把pdfjs-dist转换为es5

我试了很多,比如配置@babel/preset-env、@babel/plugin-transform-runtime等,都没用。

我认为umi默认不会对node_modules下的文件做转换,因此需要把pdfjs-dist加入到umi自身的babel转换中:

javascript 复制代码
//config.js
extraBabelIncludes: [
    /[\\/]pdfjs-dist[\\/]/,  // 匹配 node_modules/pdfjs-dist
  ]

此时打包后,可以看到async已经被转换了:

在chrome 49上也能正常访问了:

至此我们完美兼容了老版本浏览器,在此记录下,主要是排查过程。

相关推荐
盘古工具9 小时前
【分享】打开PDF文件的几种方式
windows·pdf
空中海14 小时前
01 React Native 基础、核心组件与布局体系
javascript·react native·react.js
空中海15 小时前
05 React架构设计、项目实践与专家清单
前端·react.js·前端框架
skilllite作者16 小时前
SkillLite 原生系统级沙箱功能代码导览
人工智能·chrome·后端·架构·rust
空中海17 小时前
04 工程化、质量体系与 React 生态
前端·ubuntu·react.js
空中海17 小时前
03 性能、动画与 React Native 新架构
react native·react.js·架构
空中海19 小时前
02 React Native状态、导航、数据流与设备能力
javascript·react native·react.js
优化控制仿真模型19 小时前
2026年初中英语考纲词汇表(1600词)PDF电子版
经验分享·pdf
空中海20 小时前
04 React Native工程化、质量、发布与生态选型
javascript·react native·react.js
蚁库20 小时前
PDF编辑神器,一款软件搞定PDF所有问题!
pdf