React.lazy无法加载任何组件。

7

我正在尝试在React应用程序中实现一些代码分割。虽然这在这种情况下并不严格必要,因为这是一个相当小的应用程序,但我想在低风险环境中尝试一下这个技术,然后再将其应用于更大的项目。

我的可行代码:

import React from 'react'
import { useUser } from './context/userContext'
import Layout from './components/layout'
import Routes from './components/routes'
import LandingScreen from './components/authApp/LandingScreen'

const App = () => {
  const user = useUser()

  return (
    <>
      {user ? (
        <Layout>
          <Routes />
        </Layout>
      ) : (
        <LandingScreen />
      )}
    </>
  )
}

export default App

如果我尝试将任何组件更改为使用React.lazy,例如

const LandingScreen = React.lazy(() => import('./components/authApp/landingScreen'))

我的应用程序编译没有问题,但是浏览器中没有组件渲染,并出现了以下错误:
index.js:1 The above error occurred in one of your React components:
    in Unknown (at App.js:26)
    in App (at src/index.js:14)
    in UserProvider (at appProvider.js:7)
    in AuthProvider (at appProvider.js:6)
    in AppProviders (at src/index.js:13)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/docs/error-boundaries.html to learn more about error boundaries.

我希望你能帮我找到我做错了什么。LandingScreen组件只是呈现一些和我的登录组件,但即使它只呈现一个单独的

,也会出现相同的错误。

编辑:在懒加载组件中包装组件可以解决此问题。文档似乎表明组件是可选的,但也许不是?如果有人更了解并能提出意见,那将不胜感激。


1
感谢您对文档中措辞的反馈。我已经发起了一个拉取请求来更改措辞,并可能开启一次对话:https://github.com/reactjs/reactjs.org/pull/2768 - Jamie Dixon
@JamieDixon - 但是lazy不一定要是Suspense的子组件。1. lazy组件应该Suspense的后代,但不必是其子组件(语义问题?)。2. 您可以使用其他东西来捕获抛出的Promise,Suspense只是React提供的一个很好的API。 - Adam Jenkins
2
@Adam 很好的反馈,谢谢。我在PR中加了一条注释,可能需要更改措辞以明确指出其中一个是必需的。我会推送一个新的提交,加入一些额外的措辞以使其更清晰明了。 - Jamie Dixon
1
我已经更改了PR中的措辞,使其读作“懒加载组件必须在Suspense组件或其他替代组件中呈现”。如果你有更清晰地描述它的想法,我很愿意进行讨论。 - Jamie Dixon
1
@JamieDixon 不,我认为那个可以。感谢你开启了这个PR,我认为它可能会帮助像我这样的人。 - Cal Irvine
显示剩余2条评论
1个回答

11

一个 懒加载 的组件 应该 是一个 Suspense 的子元素,如果你使用了 Suspense,那么你 需要 提供一个 fallback:

所有信息都在文档中。

const App = () => {
  const user = useUser()

  return (
    <Suspense fallback={<></>}>
      {user ? (
        <Layout>
          <Routes />
        </Layout>
      ) : (
        <LandingScreen />
      )}
    </Suspense>
  )
}


注意:你不一定要使用 Suspense,但如果不使用它,那么你基本上就必须用自己的 ErrorBoundary 来重新实现它,所以最好直接使用它。 如果任何一个在Suspense内渲染时暂停的组件(例如懒加载组件)暂停,则整个组件树会在组件准备就绪时卸载并重新挂载到最近的Suspense上。这可能是个大坑。 React提出了一个组件可以“抛出”Promise的想法-这里的 “抛出” 很重要,它不能返回Promise,必须“抛出”它-以表示它是“异步”的。如果最近的ErrorBoundary(也就是Suspense)捕获了一个Promise,那么它将显示其fallback属性。当它捕获的Promise解决时,它会呈现它的children属性-当你考虑它实际上做了什么时,这是极其简单的。
我说它一直在玩这个想法,因为即使他们已经在Suspense中实现了它,他们仍在考虑将其作为数据加载的API。确实,人们已经展示了你可以现在就那样使用它(我现在找不到链接,但这真的很酷)。 预计将在不久的将来正式规范数据获取的API。

你能详细介绍一下Suspense是什么/有什么作用吗? - Joe Lloyd
1
谢谢,我已经弄清楚了(请看我的编辑),不过文档中指出“应该”用suspense包装,这意味着它是可选的,而不是必需的。也许只是一个需要修正的措辞问题。 - Cal Irvine
我假设在服务器端渲染时无法使用Suspense,这是默认的React吗? - Joe Lloyd
@JoeLloyd - 在文档中就在这里 - Adam Jenkins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接