zhangyuang / ssr

A most advanced ssr framework support React17/React18/Vue2/Vue3 on Earth that implemented serverless-side render specification.
http://doc.ssr-fc.com/
MIT License
2.61k stars 284 forks source link

wojtekmaj/react-pdf报ReferenceError #258

Closed hechuan9 closed 1 year ago

hechuan9 commented 1 year ago

提问前请确认以下几点信息,否则你的问题将不会被解答

详细描述你的问题

我试图在我的项目中使用wojtekmaj/react-pdf: import { Document, Page } from 'react-pdf/dist/esm/entry.vite' 执行npx ssr start --vite会直接报错

Error when evaluating SSR module /web/components/pdf/all-pages.jsx:
ReferenceError: require is not defined

期望的结果

找到一种正确办法引用react-pdf

当前使用的版本

├─ ssr-common-utils@6.2.60
├─ ssr-core-react@6.2.16
├─ ssr-hoc-react@6.2.10
├─ ssr-mini-css-extract-plugin@1.6.3
├─ ssr-plugin-midway@6.2.18
├─ ssr-plugin-react@6.2.56
├─ ssr-serialize-javascript@6.0.4
├─ ssr-types@6.2.36
├─ ssr-vite-plugin-style-import@2.0.1
├─ ssr-webpack@6.2.11
├─ ssr-window@3.0.0
├─ ssr@6.2.44
├─ ssri@7.1.1
└─ webpack@4.46.0
   └─ ssri@6.0.2

复现仓库地址

超过一行代码能描述的问题必须提供复现地址, 用默认的 example 给最简单的复现代码不要给包含着一堆业务代码的仓库

你本人对问题可能的原因判断(如果你能大概判断的话)

  1. 在另一个纯React,无SSR项目是正常运行。
  2. 我的理解是SSR在compile这个react-pdf时候,需要用到pdfjs-dist,而这个文件不能正确被compile,因为它是为vite compile的。
  3. react-pdf的文档中提到可以复制pdfjs-dist,但是不知道怎么运行这个scripts,感觉也没有用https://github.com/wojtekmaj/react-pdf#standard-browserify-esbuild-and-others
  4. 文档中提到可以用whiteList, 我试了
    const userConfig: UserConfig = {
    whiteList: ['react-pdf']
    }

    但是也没什么用

zhangyuang commented 1 year ago

直接 import { Document, Page } from 'react-pdf'

zhangyuang commented 1 year ago

require is not defined 是因为你这种引入方式被当作业务代码而不是第三方代码被vite处理。直接使用 bare import。使用配置项也有办法解决但对大多数人来说理解难度过大,建议使用上面的方案或下面的方案。 参考

image

只在客户端阶段使用

hechuan9 commented 1 year ago

我试了下这段代码

import React, { useState, useEffect } from 'react'

let reactpdf = {}
export default function AllPages (props) {
  useEffect(() => {
    initReactPdf()
  }, [])

  const initReactPdf = async () => {
    reactpdf = await import('react-pdf/dist/esm/entry.vite')
    console.log('react-pdf', reactpdf)
  }

  const { pdf } = props
  console.log(props)

  return (
    123
  )
}

然后错误变成了

Uncaught (in promise) TypeError: Failed to construct 'URL': Invalid URL

我打开看了下,在react-pdf/dist/esm/entry.vite中,有句pdfjs6.GlobalWorkerOptions.workerSrc = new URL("pdfjs-dist/build/pdf.worker.js",程序是找不到pdfjs这个module。 我的理解是,pdfjs并没有在客户端被载入,这样理解对不对?请问怎么载入?

我试了默认ssr的controller,和把model调整csr的controller,但是都是同样的错误。

hechuan9 commented 1 year ago
import React, { useState, useEffect } from 'react'
import { Document, Page } from 'react-pdf'

let reactpdf = {}
export default function AllPages(props) {
  const [numPages, setNumPages] = useState(null)

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

  const { pdf } = props;
  console.log(reactpdf)

  return (
    <Document file={pdf} onLoadSuccess={onDocumentLoadSuccess}>
      {Array.from(new Array(numPages), (el, index) => (
        <Page key={`page_${index + 1}`} pageNumber={index + 1} />
      ))}
    </Document>
  )
}

BaseImport 也不行 报错是

Uncaught SyntaxError: Unexpected token '<' (at pdf.worker.js:1:1)

这个pdf.worker.js长这样

<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><title>Serverless Side Render</title><script src="/@vite/client" type="module"></script><script type="module"> import RefreshRuntime from "/@react-refresh"
RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => {}
window.$RefreshSig$ = () => (type) => type
window.__vite_plugin_react_preamble_installed__ = true</script></head><body><div id="app"></div><script>window.prefix="/";</script><script>window.__USE_VITE__=true</script><script type="module" src="/node_modules/ssr-plugin-react/esm/entry/client-entry.js"></script></body></html>
hechuan9 commented 1 year ago

我在FAQ里看到了,onlyCsr个选项,我的理解是也可以实现获取客户端的react-pdf的功能?

import React, { useState, useEffect } from 'react'
import { Document, Page } from 'react-pdf/dist/esm/entry.vite'
import { onlyCsr } from 'ssr-hoc-react'

function PdfViewer (props) {
  const [numPages, setNumPages] = useState(null)

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

  const { pdf } = props
  console.log(pdf)

  return (
    <Document file={pdf} onLoadSuccess={onDocumentLoadSuccess}>
      {Array.from(new Array(numPages), (el, index) => (
        <Page key={`page_${index + 1}`} pageNumber={index + 1} />
      ))}
    </Document>
  )
}

export default onlyCsr(PdfViewer)

但是试了好像并没有用,好像依然在服务器端在载入?

zhangyuang commented 1 year ago

步骤一

pdf.worker.js 移动到本地node静态资源文件夹

  "start:vite": "cp ./node_modules/pdfjs-dist/build/pdf.worker.js ./public && ssr start --vite",
   "build:vite": "cp ./node_modules/pdfjs-dist/build/pdf.worker.js ./public && ssr build --vite",

步骤二

设置worker的文件路径,在客户端阶段渲染具体的pdf组件。不使用 entry.vite,其中的 new url import.meta 语法解析有问题

import React, { useContext, useState, useEffect } from 'react'
import { SProps, IContext } from 'ssr-types'
import { pdfjs } from 'react-pdf'
pdfjs.GlobalWorkerOptions.workerSrc = 'pdf.worker.js'
let Document = () => (<></>)
let Page = () => (<></>)

export default (props: SProps) => {
  const [refresh, setRe] = useState(false)
  useEffect(() => {
    init()
  })
  const init = async () => {
    const pdf = await import('react-pdf/dist/esm/entry')
    Document = pdf.Document
    Page = pdf.Page
    setRe(true)
  }
  const [numPages, setNumPages] = useState(null)
  const [pageNumber, setPageNumber] = useState(1)

  function onDocumentLoadSuccess ({ numPages }) {
    setNumPages(numPages)
  }
  return (
    <div>
      <Document file="report.pdf" onLoadSuccess={onDocumentLoadSuccess}>
        <Page pageNumber={pageNumber} />
      </Document>
    </div>
  )
}
hechuan9 commented 1 year ago

我试了一下大佬的代码,但是还是出现同样的错误,我最后用了外部的pdf.worker.min.js让这个程序work的。

import React, { useState } from 'react'
import { Document, Page, pdfjs } from 'react-pdf'
pdfjs.GlobalWorkerOptions.workerSrc = '//unpkg.com/pdfjs-dist@2.16.105/build/pdf.worker.min.js'

function PdfViewer (props) {
  const [numPages, setNumPages] = useState(null)

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

  const { pdf } = props

  return (
    <Document file={pdf} onLoadSuccess={onDocumentLoadSuccess}>
      {Array.from(new Array(numPages), (el, index) => (
        <Page key={`page_${index + 1}`} pageNumber={index + 1} />
      ))}
    </Document>
  )
}

export default PdfViewer

试了下csr和ssr都能成功render。现在还有个小问题,外部的链接不是很可靠,有什么办法能把该文件放到本地并访问的? 我试了大佬提供的 "start:vite": "cp ./node_modules/pdfjs-dist/build/pdf.worker.js ./public && ssr start --vite", 但是并不能用,好像有个语法错误,但是改成,"start:vite": "cp ./node_modules/pdfjs-dist/build/pdf.worker.js ./public/. && ssr start --vite"也找不到这个文件。

hechuan9 commented 1 year ago

hmm,我搞定了,就是把pdf.worker.min.js复制到public/js文件夹里,然后引用改成pdfjs.GlobalWorkerOptions.workerSrc = '/js/pdf.worker.min.js'

可以关闭这个issue了...