原文: blog.bitsrc.io/lazy-loadin…

image.png
_
虽然在 React 16.8.1 中终于面世的 hooks 引人瞩目,但在去年发布的 16.6.0 版本里也包含了一个吸引人的新特性,可以让我们在不依赖第三方库的情况下简化对延迟加载(lazy loading)的处理。

让我们看看如何借助这个特性改善应用性能,并构建更好的用户体验。

按照过往的经验,在构建组件的时候,将其用类似 Bit 的工具归纳起来是很有用的 — 可以分享你的组件并在任意应用中使用之,以加速开发并保持 DRY 原则。

React.lazy() 是什么?

这项新功能使得可以不借助任何附加库就能通过代码分割(code splitting)延迟加载 react 组件。延迟加载是一种优先渲染必须或重要的用户界面项目,而将不重要的项目悄然载入的技术。这项技术已经被完全整合进了 react 自身的核心库中。之前我们要用 react-loadable 达到这一目的,但现在用 react.lazy() 就行了。

Suspense 挂起组件

Suspense 是一个延迟函数所必须的组件,通常用来包裹住延迟加载组件。多个延迟加载的组件可被包在一个 suspense 组件中。它也提供了一个 fallback 属性,用来在组件的延迟加载过程中显式某些 react 元素。

延迟和挂起为何重要?

首先,打包工具将所有代码组件相继归纳到一个 javascript 块中,并将其传递给浏览器;但随着应用增长,我们注意到打包的体积也与日俱增。这会导致应用因为加载慢而难以使用。借助代码分割,代码包能被分割成更小的块,最重要的块先被加载,而其余次要的则延迟加载。

同时,我们知道构建应用的一个最佳实践是:应该考虑到用户在使用移动互联网数据和其他慢速网络连接时的情况。作为开发者就应该在哪怕是在资源读取到 DOM 中的挂起阶段也能控制好用户体验。

起步

根据 react 官方文档,如果使用了下列技术,那么就已经有 webpack 打包配置了:

  • CRA (create react app)
  • Next js
  • Gatsby

如果没有的话,就需要自己设置打包了。比如,读一下 Webpack 官方文档中的 InstallationGetting Started

Demo

我们用 create-react-app 创建一个 react 应用,并在里面实现带挂起的延迟加载,它将用来显示 MTV Base 上 2019 上头牌艺人的专辑名和专辑数量。我用 create-react-app 创建了一个干净的应用,并包含了一个我们可以在本例中用得上的简易组件。

  • 克隆 gitlab.com/viclotana/r…
  • 解压文件并打开一个终端窗口
  • 在解压出的文件的根目录下安装项目的 node modules 依赖
  • 用以下命令启动开发服务器:
  1. $ sudo npm start

[译] 使用 React.lazy 和 suspense 延迟加载 React 组件 - 图2

就是这么个简单的应用,艺人的数据被从应用中的一个 store 中读出。当然你也可以自己编写这些代码,应用的 src 下面应该有这些文件:

  1. Artists.js
  1. import React from react’;
  2. import ‘./App.css’;
  3. import artists from “./store”;
  4. export default function Artists(){
  5. return (
  6. <>
  7. <h1>MTV Base Headline Artists 2019</h1>
  8. {artists.map(artist =>(
  9. <div id=”card-body key={artist.id}>
  10. <div className=”card”>
  11. <h2>{artist.name}</h2>
  12. <p>genre: {artist.genre}</p>
  13. <p>Albums released: {artist.albums}</p>
  14. </div>
  15. </div>
  16. ))}
  17. </>
  18. );
  19. }
  1. Store.js
  1. export default [
  2. {
  3. id: 1”,
  4. name: Davido”,
  5. country: Nigeria”,
  6. genre: Afro-Pop”,
  7. albums: 2
  8. },
  9. {
  10. id: 2”,
  11. name: AKA”,
  12. country: South-Africa”,
  13. genre: Hip-Hop”,
  14. albums: 4
  15. },
  16. {
  17. id: 3”,
  18. name: Seyi Shay”,
  19. country: Nigeria”,
  20. genre: R&B”,
  21. albums: 2
  22. },
  23. {
  24. id: 4”,
  25. name: Sauti Sol”,
  26. country: Kenya”,
  27. genre: Soul”,
  28. albums: 3
  29. }
  30. ];
  1. Index.js
  1. import React from react’;
  2. import ReactDOM from react-dom’;
  3. import ‘./index.css’;
  4. import Artists from ‘./Artists’;
  5. class App extends React.Component {
  6. render(){
  7. return(
  8. <div className=”App”>
  9. <Artists />
  10. </div>
  11. );
  12. }
  13. }
  14. ReactDOM.render(<App />, document.getElementById(‘root’));
  1. App.css
  1. .App {
  2. text-align: center;
  3. }
  4. h1 {
  5. padding: 30px;
  6. }
  7. #card-body {
  8. display: inline-flex;
  9. padding: 10px;
  10. margin: 30px 30px;
  11. border: 5px solid rgb(93, 171, 207);
  12. border-radius: 8px;
  13. background: lightblue;
  14. }

现在让我们看看如何用 react.lazy 及 suspense 去处理艺人组件的延迟加载。

  • index.js 的头部引入 react 中的 lazy 和 suspense:
  1. import { Suspense, lazy } from 'react';
  • 要像常规组件一样渲染一个动态引入的组件,使用 react 文档中提供的 react.lazy 函数语法,如下:
  1. const OtherComponent = React.lazy(() => import('./OtherComponent'));
  2. function MyComponent() {
  3. return (
  4. <div>
  5. <OtherComponent />
  6. </div>
  7. );
  8. }
  • 应用到我们的艺人组件上:
  1. const Artists = React.lazy(() => import('./Artists'));
  2. function MyComponent() {
  3. return (
  4. <div>
  5. <Artists />
  6. </div>
  7. );
  8. }

若在 App 组件渲染期间,包含艺人组件的模块没有加载完,就必须显示一些提示等待的 fallback 内容 — 比如一个加载指示器,下面是用 Suspense 组件完成这一目的的语法:

  1. const OtherComponent = React.lazy(() => import('./OtherComponent'));
  2. function MyComponent() {
  3. return (
  4. <div>
  5. <Suspense fallback={<div>Loading...</div>}>
  6. <OtherComponent />
  7. </Suspense>
  8. </div>
  9. );
  10. }

应用到艺人组件上:

  1. const Artists = React.lazy(() => import('./Artists'));
  2. function MyComponent() {
  3. return (
  4. <div>
  5. <Suspense fallback={<div>Loading...</div>}>
  6. <Artists />
  7. </Suspense>
  8. </div>
  9. );
  10. }

把以上拼在一起, index.js 看起来是这样的:

  1. import React, { lazy, Suspense } from react’;
  2. import ReactDOM from react-dom’;
  3. import ‘./index.css’;
  4. const Artists = lazy(() => import(‘./Artists’))
  5. class App extends React.Component {
  6. render(){
  7. return(
  8. <div className=”App”>
  9. <Suspense fallback={<h1>Still Loading…</h1>}>
  10. <Artists />
  11. </Suspense>
  12. </div>
  13. );
  14. }
  15. }
  16. ReactDOM.render(<App />, document.getElementById(‘root’));

在你的 localhost 上应该运行的很快,以至于你根本感觉不到有什么改变。但你可以创建一段时间统计代码,或模拟慢速网络:

  • 打开浏览器的 dev tools
  • 选择 network tab
  • 点击右侧远端的 online tab,显示其他选项(最右侧的下箭头)
  • 选择 fast 3G

[译] 使用 React.lazy 和 suspense 延迟加载 React 组件 - 图3

现在刷新浏览器就能看到延迟加载如何发生了…

[译] 使用 React.lazy 和 suspense 延迟加载 React 组件 - 图4

多个延迟加载组件

那么再快速添加一个渲染标题的小组件,看看 react.lazy 如何仍只用一个 suspense 组件处理:

创建 performers.js 文件:

  1. mport React from react’;
  2. import ‘./App.css’;
  3. export default function Performers(){
  4. return (
  5. <>
  6. <h2>These are the MTV Base Headline Artists...</h2>
  7. </>
  8. );
  9. }

并在 index.js 中添加一行延迟加载代码:

  1. import React, { lazy, Suspense } from react’;
  2. import ReactDOM from react-dom’;
  3. import ‘./index.css’;
  4. const Artists = lazy(() => import(‘./Artists’))
  5. const Performers = lazy(() => import(‘./Performers’))
  6. class App extends React.Component {
  7. render(){
  8. return(
  9. <div className=”App”>
  10. <Suspense fallback={<h1>Still Loading…</h1>}>
  11. <Artists />
  12. <Performers />
  13. </Suspense>
  14. </div>
  15. );
  16. }
  17. }
  18. ReactDOM.render(<App />, document.getElementById(‘root’));

现在,在 suspense 中的占位符元素渲染之后,两个延迟加载的组件便立刻显示出来了。

[译] 使用 React.lazy 和 suspense 延迟加载 React 组件 - 图5

这和 loadable 中必须为每个延迟加载组件都弄个 loading 是不同的。

⚠️ 重要提示

React.lazy 和 Suspense 在服务端渲染中尚不可用。如果想在服务器渲染的应用中使用代码分割,Loadable 组件仍是强烈推荐的,在其文档中有很好相关解释。

总结

我们看到了如何用 react 提供的 lazy 和 suspense 组件实现延迟加载。和这个新特性带来的众多可能性相比,以上例子过于基础。你可以在自己的项目中灵活应用,编码愉快!