How to use React lazy code splitting
React.lazy why?
lazy
The import() function allows us to dynamically import components. We might want to do this to reduce the initial bundle size that the user must download to see the content on the screen.
Let's say our application is split into several routes. You have a /home route and a /large-page route. The /large-page route imports and uses some large library that we don't use on the /home page. If a user visits our /home page, we don't want them to have to download the large library in order to render the content on the screen - they might not even visit your /large-page route, so it's a waste.
What will happen is you load enough javascript to render the /home route for a quick initial render, then if the user navigates to the /large-page route you will show a loading spinner to inform the user that some transitions are about to happen and load the chunk of javascript needed for the /large-page route.
Most people on the internet are used to having to wait for transitions when navigating between pages. What's even worse user experience is having our users looking at a white blank screen for too long.
So let's see React.lazy
how we can help with this.
Example
Let's create a React application:
$ npx create-react-app react-lazy --template typescript
$ cd react-lazy
$ npm install react-router-dom
$ npm install moment
$ npm install --save-dev @types/react-router-dom
$ npm start
You only need the index.tsx and App.tsx files, you can delete the .css and .test files.
Let's take a look at the contents of src/index.tsx
src/index.tsx
import React from 'react'; import ReactDOM from 'react-dom'; import {App} from './App'; ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root'), );
And the contents of src/App.tsx :
src/App.tsx
import {BrowserRouter as Router, Link, Route, Switch} from 'react-router-dom'; import Home from './Home'; import LargePage from './LargePage'; export function App() { return ( <Router> <div> <Link to="/">Home</Link> <hr /> <Link to="/large-page">Large Page</Link> <hr /> </div> <Switch> <Route exact path="/"> <Home /> </Route> <Route exact path="/large-page"> <LargePage /> </Route> </Switch> </Router> ); }
First, we react-router-dom
import some components from , then we import some local components that we haven't written yet, then we have a simple navigation with 2 links and our components on the / and /large-page routes. Now let's add our components. First let's create the src/Home.tsx component:
src/Home.tsx
export default function Home() { return <h1>This is the home page...</h1>; }
and the src/LargePage.tsx component:
src/LargePage.tsx
import * as moment from 'moment'; export default function LargePage() { const a = moment.duration(-1, 'week').humanize(true, {d: 7, w: 4}); // a week ago return ( <div> <h1>{a}</h1> </div> ); }
In our LargePage
Component, we import the library and use it, but we don't need the library in moment
our Homemoment
route . Now we can navigate between routes, but even if the user never goes to the LargePage route, they still have to download the moment library on the initial render.
Now let's change this behavior. We only want the component code for the Moment library and LargePage routes to be downloaded when the user navigates to that route. Let's edit src/App.tsx to:
src/App.tsx
- import Home from './Home'; - import LargePage from './LargePage'; + import {lazy} from 'react'; + const Home = lazy(() => import('./Home')); + const LargePage = lazy(() => import('./LargePage'));
Adding suspense borders
If you look in your browser now, you should see a big error.
Error: A react suspended component while rendering, but no fallback UI was specified. Add a Suspense fallback= component higher in the tree to provide a loading indicator or placeholder to display..
So react is telling us that a component is “ suspended ”, but we haven’t provided a loading component to render while that component is suspended. Suspended means that the component is not ready to render because its requirements have not yet been met. Going to the Home route, opening devtools , selecting the Network tab and filtering for JS files, we can see that the Home route has a separate JS block because we are lazily importing the Home component. React tries to render it, but it hasn’t loaded yet, so the component is suspended and must show a loading state fallback component, but we haven’t provided one.
By the way, the homepage component is so small that we shouldn’t load it lazily. An extra network request for a module of 1Kb in size is a waste. We’re doing it just for the sake of the example.
Suspense lets our components wait for something before rendering, showing a fallback while waiting. Let's see how it works, edit the src/App.tsx page again and change it to:
src/App.tsx
import {lazy, Suspense} from 'react'; import {BrowserRouter as Router, Link, Route, Switch} from 'react-router-dom'; const Home = lazy(() => import('./Home')); const LargePage = lazy(() => import('./LargePage')); export function App() { return ( <Router> <div> <Link to="/">Home</Link> <hr /> <Link to="/large-page">Large Page</Link> <hr /> </div> {/* Now wrapping our components in Suspense passing in a fallback */} <Suspense fallback={<h1>Loading...</h1>}> <Switch> <Route exact path="/"> <Home /> </Route> <Route exact path="/large-page"> <LargePage /> </Route> </Switch> </Suspense> </Router> ); }
All we have to do is wrap our component in a Suspense boundary and pass a fallback for the loading state.
If we open the Network tab in devtools, set our network speed to slow 3G and refresh the page, you should be able to see our fallback being rendered to the page, along with the Home route’s JS chunk being loaded.
或者,如果我们在浏览器中安装了 react devtools 扩展,您可以手动挂起该组件。点击 react 扩展中的 Components 选项卡,选择 Home 组件,然后点击右上角的秒表图标,将选中的组件挂起。
到目前为止一切顺利,现在让我们再次打开网络选项卡,按 JS 文件过滤并导航到大页面路由。你会看到我们加载了 2 个 JS 块,一个用于组件本身,另一个用于时刻库。
在这种状态下,当我们导航到大页面路由时,我们的应用程序会延迟加载时刻库。如果用户访问我们的主页并且从未访问过大页面,他们甚至不必加载即时库或大页面组件的代码。
作为旁注,用户只需加载一次 JS 块。如果它们来回导航,浏览器已经缓存了文件,我们将看不到后备加载微调器,组件不必暂停。
添加错误边界
我们的应用程序似乎处于良好状态,但是我们正在使用网络来请求我们拆分的 JS 文件,所以如果用户加载我们的主页,失去与互联网的连接并导航到 LargePage 路由会发生什么。
要对此进行测试,请转到主页,刷新,打开我们的网络选项卡并将网络状态设置为离线。现在导航到 /large-page 路径,我们应该会看到一个空白屏幕,这绝不是一件好事。
在我们的控制台中,我们收到以下错误:
Uncaught ChunkLoadError: Loading chunk 2 failed.
所以我们尝试加载 JS 块,但失败了,整个应用程序崩溃了。为了向用户提供一些反馈并记录错误以便我们修复它,我们必须包装我们的组件,这些组件可能会抛出一个 ErrorBoundary
组件。
ErrorBoundary
就像一个 try{} catch(){}
用于在它下面的组件的渲染方法中抛出的错误。
考虑它的一个好方法是:Suspense 边界在其子级尚未准备好渲染时显示一个
FallbackComponent
- 它处理加载状态,而ErrorBoundary
处理在组件的渲染方法中引发的错误。
让我们在 src/ErrorBoundary.tsx 中添加一个 ErrorBoundary
组件:
src/ErrorBoundary.tsx
import React from 'react'; export class ErrorBoundary extends React.Component< {children?: React.ReactNode}, {error: unknown; hasError: boolean} > { state = {hasError: false, error: undefined}; componentDidCatch(error: any, errorInfo: any) { this.setState({hasError: true, error}); } render() { if (this.state.hasError) { return <h1>An error has occurred. {JSON.stringify(this.state.error)}</h1>; } return this.props.children; } }
让我们在 src/App.tsx 组件中使用它:
src/App.tsx
import {ErrorBoundary} from './ErrorBoundary'; // ... export function App() { return ( <Router> <div> <Link to="/">Home</Link> <hr /> <Link to="/large-page">Large Page</Link> <hr /> </div> {/* Now wrapping our components in Suspense passing in a fallback */} <ErrorBoundary> <Suspense fallback={<h1>Loading...</h1>}> <Switch> <Route exact path="/"> <Home /> </Route> <Route exact path="/large-page"> <LargePage /> </Route> </Switch> </Suspense> </ErrorBoundary> </Router> ); }
现在我们已经包装了可能会抛出 ErrorBoundary
的组件。 让我们重复测试:刷新主页,打开网络选项卡,将网络设置设置为离线,然后导航到 /large-page 路由。 我们将看到一个错误被打印到屏幕上,这比看到空白屏幕更好。
限制
-
ErrorBoundary
组件是一个类,在撰写本文时,我们只能使用类来实现错误边界。 -
我们延迟加载的组件是默认导出 -
React.lazy
当前仅支持默认导出。
总结
React.lazy
允许我们将代码拆分成块。 为了提高大型应用程序的性能,我们不想强迫用户下载包含我们整个应用程序的单个 JS 文件,因为他们可能不会使用我们的整个应用程序,他们不会访问我们网站的每条路由。
由于互联网上的大多数人习惯于在页面转换之间等待,因此提供一个加载指示器并在用户导航到它时按需加载组件代码比让所有用户加载他们可能不需要的代码更好。
For reprinting, please send an email to 1244347461@qq.com for approval. After obtaining the author's consent, kindly include the source as a link.
Related Articles
React Tutorial - Transferring Props
Publish Date:2025/03/16 Views:185 Category:React
-
React transfers Props. Props are generated when components are encapsulated. Components expose some properties (Props) to the outside world to complete some functions.
React Tutorial: Props Anti-Pattern
Publish Date:2025/03/16 Views:183 Category:React
-
React's Props anti-pattern, using Props to generate state in getInitialState is an anti-pattern - Anti-Pattern.
React Tutorial - Props Validation
Publish Date:2025/03/16 Views:99 Category:React
-
Props validation is a very useful way to use components correctly. It can avoid many bugs and problems as your application becomes more and more complex. In addition, it can make your program more readable.
React tutorial: Types of Props for child components
Publish Date:2025/03/16 Views:170 Category:React
-
Usually, the child components of a React component are a group, that is, the child components are an array. Introduction to Type of the Children Props.
How to solve the error Uncaught TypeError: Cannot read properties of undefined in
Publish Date:2025/03/16 Views:150 Category:React
-
In the process of React development, we often encounter some errors. Here we look at an error reported in App.js. The error is as follows: App.js:69 Uncaught TypeError: Cannot read properties of undefined (reading 'setState') at onInput
Why do you need to bind event handlers in React Class Components?
Publish Date:2025/03/16 Views:58 Category:React
-
When using React, we must have come across control components and event handlers. We need to use `.bind()` in the constructor of the custom component to bind these methods to the component instance. As shown in the following code:
Solution to the error "does not contain a default export" in React
Publish Date:2025/03/16 Views:187 Category:React
-
When we try to use `default import` to import from a module that does not have a `default export`, we get a "does not contain a default export" error. To fix the error, make sure the module has named exports and wrap the import in curly braces, e.g.
Error in React: Attempted import error 'X' is not exported from Solution
Publish Date:2025/03/16 Views:76 Category:React
-
In React, the error “Attempted import error 'X' is not exported from” in React.js occurs when we try to import a named import that does not exist in the specified file. To fix the error, make sure the module has named exports and you have not obfu
Solve the Module not found: Can't resolve 'react-bootstrap' error
Publish Date:2025/03/16 Views:85 Category:React
-
To resolve the error "Module not found: Error: Can't resolve 'react-bootstrap'", make sure to install the react-bootstrap package by opening a terminal in the root directory of the project and running the command `npm install react-bootstrap bootstrap