使用 Lazy + Suspense 反应预加载组件
React preloading components with Lazy + Suspense
我目前正在使用带有 Suspense 和 Lazy 的 React 16 来对我的代码库进行代码拆分。虽然我想预加载组件。
在我下面的示例中,我有两条路线。有没有办法在 Prime
挂载后立即预加载 Demo
?我试图在 Prime
页面的 componentDidMount
中创建另一个动态导入,但 React.lazy
似乎没有得到与下面的动态导入相同的文件。
import React, { lazy, Suspense } from 'react';
import { Switch, Route, withRouter } from 'react-router-dom';
import GlobalStyle from 'styles';
import Loading from 'common/Loading';
const Prime = lazy(() => import(/* webpackChunkName: "Prime" */'modules/Prime'));
const Demo = lazy(() => import(/* webpackChunkName: "Demo" */'modules/Demo'));
const App = () => (
<main>
<GlobalStyle />
<Suspense fallback={<Loading>Loading...</Loading>}>
<Switch>
<Route path="/" component={Prime} exact />
<Route path="/demo" component={Demo} />
</Switch>
</Suspense>
</main>
);
export default withRouter(App);
所以我尝试了不同的方法,例如使用和不使用 webpackChunkName
以及在 componentDidMount
中导入其他组件的不同方法,如下所示。在 componentDidMount
中导入文件的前两种方法导致 Webpack 错误,如下图底部所示。只有第三个进行了,但是在图像中制作了文件2.[hash].js
,仅在访问页面后加载,而不是componentDidMount
我在这里错过了什么?
modules/Demo.jsx
代码:
import React from 'react';
import LogoIcon from 'vectors/logo.svg';
import PageLink from 'common/PageLink';
import Anchor from 'common/Anchor';
import CenteredSection from 'common/CenteredSection';
const Demo = () => (
<CenteredSection variant="green">
<LogoIcon />
<PageLink to="/" variant="green">Go to home page</PageLink>
</CenteredSection>
);
export default Demo;
不确定这会有多大帮助,但这是一个有效的 code sandbox(演示由 componentDidMount 加载)。它是使用 create-react-app 进行配置的代码的相当简化的版本。也许您可以以此为起点,逐渐将其变形为更接近您的应用程序,以查看导致动态导入不再按预期工作的原因。
这非常容易做到,我认为对 lazy() 和 Suspense 在幕后所做的事情存在误解。
React.lazy() 的唯一期望是它接受一个函数,returns 一个使用默认组件解析的 Promise。
React.lazy(() => Promise<{default: MyComponent}>)
所以如果你想预加载,你所要做的就是提前自己执行promise。
// So change this, which will NOT preload
import React from 'react';
const MyLazyComp = React.lazy(() => import('./path/to/component'));
/*********************************************/
// To this, which WILL preload
import React from 'react';
// kicks off immediately when the current file is imported
const componentPromise = import('./path/to/component');
// by the time this gets rendered, your component is probably already loaded
// Suspense still works exactly the same with this.
const MyLazyComp = React.lazy(() => componentPromise);
这是一个已知的签名这一事实使其在各种其他情况下都很有用。例如,我有一堆组件依赖于动态加载的 google 映射 api,我能够创建一个函数来加载 google 映射 api 和然后导入组件。我不会详细说明这个例子的内部结构,因为它是一个切线,但关键是我让自己成为一个函数,它执行一堆异步的事情,然后 returns 一个带有 {default: Component} 对象的 Promise。
import React from 'react';
const MyLazyComp = React.lazy(() => importMapsComponent('./path/to/comp'));
我目前正在使用带有 Suspense 和 Lazy 的 React 16 来对我的代码库进行代码拆分。虽然我想预加载组件。
在我下面的示例中,我有两条路线。有没有办法在 Prime
挂载后立即预加载 Demo
?我试图在 Prime
页面的 componentDidMount
中创建另一个动态导入,但 React.lazy
似乎没有得到与下面的动态导入相同的文件。
import React, { lazy, Suspense } from 'react';
import { Switch, Route, withRouter } from 'react-router-dom';
import GlobalStyle from 'styles';
import Loading from 'common/Loading';
const Prime = lazy(() => import(/* webpackChunkName: "Prime" */'modules/Prime'));
const Demo = lazy(() => import(/* webpackChunkName: "Demo" */'modules/Demo'));
const App = () => (
<main>
<GlobalStyle />
<Suspense fallback={<Loading>Loading...</Loading>}>
<Switch>
<Route path="/" component={Prime} exact />
<Route path="/demo" component={Demo} />
</Switch>
</Suspense>
</main>
);
export default withRouter(App);
所以我尝试了不同的方法,例如使用和不使用 webpackChunkName
以及在 componentDidMount
中导入其他组件的不同方法,如下所示。在 componentDidMount
中导入文件的前两种方法导致 Webpack 错误,如下图底部所示。只有第三个进行了,但是在图像中制作了文件2.[hash].js
,仅在访问页面后加载,而不是componentDidMount
我在这里错过了什么?
modules/Demo.jsx
代码:
import React from 'react';
import LogoIcon from 'vectors/logo.svg';
import PageLink from 'common/PageLink';
import Anchor from 'common/Anchor';
import CenteredSection from 'common/CenteredSection';
const Demo = () => (
<CenteredSection variant="green">
<LogoIcon />
<PageLink to="/" variant="green">Go to home page</PageLink>
</CenteredSection>
);
export default Demo;
不确定这会有多大帮助,但这是一个有效的 code sandbox(演示由 componentDidMount 加载)。它是使用 create-react-app 进行配置的代码的相当简化的版本。也许您可以以此为起点,逐渐将其变形为更接近您的应用程序,以查看导致动态导入不再按预期工作的原因。
这非常容易做到,我认为对 lazy() 和 Suspense 在幕后所做的事情存在误解。
React.lazy() 的唯一期望是它接受一个函数,returns 一个使用默认组件解析的 Promise。
React.lazy(() => Promise<{default: MyComponent}>)
所以如果你想预加载,你所要做的就是提前自己执行promise。
// So change this, which will NOT preload
import React from 'react';
const MyLazyComp = React.lazy(() => import('./path/to/component'));
/*********************************************/
// To this, which WILL preload
import React from 'react';
// kicks off immediately when the current file is imported
const componentPromise = import('./path/to/component');
// by the time this gets rendered, your component is probably already loaded
// Suspense still works exactly the same with this.
const MyLazyComp = React.lazy(() => componentPromise);
这是一个已知的签名这一事实使其在各种其他情况下都很有用。例如,我有一堆组件依赖于动态加载的 google 映射 api,我能够创建一个函数来加载 google 映射 api 和然后导入组件。我不会详细说明这个例子的内部结构,因为它是一个切线,但关键是我让自己成为一个函数,它执行一堆异步的事情,然后 returns 一个带有 {default: Component} 对象的 Promise。
import React from 'react';
const MyLazyComp = React.lazy(() => importMapsComponent('./path/to/comp'));