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上也能正常访问了:

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

相关推荐
崔庆才丨静觅14 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606114 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了14 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅15 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅15 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅15 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment15 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅16 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊16 小时前
jwt介绍
前端
爱敲代码的小鱼16 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax