zl程序教程

您现在的位置是:首页 >  工具

当前栏目

懒加载组件

组件 加载
2023-09-27 14:28:54 时间

React 16.6 的新发布带来了一些只需很小努力就能给React组件对增加了很多力量的新特性。

其中有两个是 React.SuspenseReact.lazy(), 这个很容易用在代码分割和懒加载上。

这篇文章关注在如何在 React 应用中使用两个新特性和他们给 React 开发者带来的新的潜力。

代码分割

过去几年写 JavaScript 应用的方式进化了。在 ES6(modules)的出现,Babel 编译器,和其他打包工具像是 WebPack 和Browserify,JavaScript 应用现在可以用完全现代化的模式写出容易维护的东西。

通常,每个模块被导入合并在一个文件叫做 bundle,这些 bundle 在一张页面上包括了整个APP。然而,当 APP 增长的时候,这些 bundle 尺寸开始变得越来越大,因此影响了页面加载时间。

打包工具像是 Webpack 和 Browserify 提供了代码分割的支持,可以在需要加载(懒加载)而不是一次性加载不同的 bundles 中引入分割代码,从而提高 app 的表现。

Dynamic Imports

代码风格的主要方式之一是使用动态导入。动态导入作用于 import() 语法,这还不是 JavaScript 语言标准的一部分,但是一个期望不久被接受的提案。

调用 import() 去加载模块依赖 JavaScript 的 Promises。因此,返回一个完整的加载的模块或者如果模块不存在的话就拒绝。

对于老的浏览器,es6-promise 补充应该用来补充 Promise

这儿有一个用 Webpack 打包的app的内容,看起来是动态导入模块:

import(/* webpackChunkName: "moment" */ 'moment')
.then(({default: moment}) => {
  const tommorrow =moment().startOf('day').add(1, 'day');
  return tomorrow.format('LLL');
})
.catch(error => console.error("..."))
复制代码

当 Webpack 看到这样的语法,它会为 moment 库,动态创建一个分割包。

对于 React 应用,如果使用 create-react-app 或者 Next.js,代码分割在 import()中悄悄产生。

然而,如果自定义了 Webpack的设置,你需要检查 Webpack 指导。对于 Babel 转化,你需要 yarnpkg.com/en/package/… 插件,允许 Babel 正确解析 import()

React 组件的代码分割

已经有几种技术应用于 React 组件的代码分割上。常见的实现是动态 import()在应用中懒加载路由组件——这个通常是作为基于路由代码分割的组件。

然而,这里有个叫 React-loadable 的非常流行的包用于 React 组件的代码分割。它提供一个高阶函数用 promise 来加载 React 组件,实现动态 import() 语法。

考虑下面叫做 MyComponent 的 React 组件:

import OtherComponent from './OtherComponent';

export defautl function MyComponent() {
  return (
    <div>
      <h1>My Component</h1>
      <OtherComponent />
    </div>
  )
}
复制代码

这里,OtherComponent 是不会请求直到MyComponent开始渲染。然而,因为我们静态导入了 OtherComponent,它会和 MyComponent 一起打包。

我们可以使用 react-loadable 去延迟加载 OtherComponent,直到我们渲染MyComponent,从而代码分割成几个包。这里有个用 react-loadable 懒加载的OtherComponent

impoort Loadable from 'react-loadable';

const LoadableOtherComponent = loadable({
  loader: () => import('./OtherComponent'),
  loading: () => <div>Loading...</div>
});

export default function MyComponent() {
  return (
    <div>
      <h1>My Component</h1>
      <LoadableOtherComponent/>
    </div>
  )
}
复制代码

在这里能看到在选择对象中,组件被动态 import() 语法导入,赋值给 loader 属性。

React-loadable 也是用了 loading 属性去具体指出当等待真正组件加载时,将会渲染的回调组件。

你可以在这篇文档中了解你能通过 react-loadable 实现什么。

使用 Suspense 和 React.lazy()

在 React 16.6 中,支持基础组件的代码分割和懒加载已经通过 React.lazy()React.Suspense 添加。

React.lazy() 和 Suspense 还没有支持服务端。服务端的代码分割,仍然使用 React-Loadable。

React.lazy()

React.lazy() 很容易创建一个使用动态 import 的组件,而且像常规组件一样渲染。当组件被渲染时,它会自动打包包含这个加载的组件。

当调用 import() 加载组件,React.lazy()` 使用一个必须返回一个 promise 的参数的方法。这个默认导出包含 React 组件返回的 Promise 处理了模块。

当使用 React.lazy() 时,看起来像:

// 不使用 React.lazy()

import OtherComponent from './OtherComponent';

const MyComponent = () => {
  <div>
    <OtherComponent />
  </div>
};

// 使用 React.lazy()

const OtherComponent = React.lazy(() => import('./OtherComponent'));

const MyComponent = () => {
  <div>
    <OtherComonment />
  </div>
}
复制代码

Suspense

一个使用 React.lazy() 的组件只会在它需要的时候被加载。

因此,这里需要展示一些占位符内容的格式,当懒加载组件正在被加载的时候,比如用一个加载指示器。 这就是 React.Suspense 所创建的。

React.Suspense 是一个包裹了懒加载组件的组件。你可以在不同的层级上使用一个 Suspense 组件包裹多个懒加载组件。

当所有懒加载组件加载后,这个 Suspense 组件使用 fallback 属性可以接受任何你想渲染的组件作为一个占位符。

import React, { Suspense } from 'react';

const LazyComponent = React.lazy(() => import('./OtherComponent'));

const MyComponent = () => {
  <div>
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  </div>
}
复制代码

如果组件懒加载失败,在懒加载之上放置明显的错误边界来展示不错的用户体验。

我在 CodeSandbox 上已经创建了一个很简单的例子来演示使用 React.lazy()Suspense 作为懒加载组件。

这里有个微型的app代码:

import React, { Suspense } from "react";
import Loader from "./components/Loader";
import Header from "./components/Header";
import ErrorBoundary from "./components/ErrorBoundary";

const Calendar = React.lazy(() => {
  return new Promise(resolve => setTimeout(resolve, 5 * 1000)).then(
    () =>
      Math.floor(Math.random() * 10) >= 4
        ? import("./components/Calendar")
        : Promise.reject(new Error())
  );
});

export default function CalendarComponent() {
  return (
    <div>
      <ErrorBoundary>
        <Header>Calendar</Header>

        <Suspense fallback={<Loader />}>
          <Calendar />
        </Suspense>
      </ErrorBoundary>
    </div>
  );
}
复制代码

这里,一个很简单的 Loader 组件在懒加载 Calendar 组件中被创建用作回调内容。当懒组件 Calendar 加载失败,一个边界提示被创建来展示友好的错误。

我这里包裹了懒加载日历来模拟5秒延时。为了增加 Calendar 组件加载失败的概率,我也使用一个条件导入 Calendar 组件,或者返回一个promise的rejects。

const Calendar = React.lazy(() => {
  return new Promise(resolve => setTimeout(resolve, 5 * 1000)).then(
    () => Math.floor(Math.random() * 10 )>= 4 ?
     import("./components/Calendar"):
     Promise.reject(new Error())
  )
})
复制代码

下面的截屏展示了当渲染的时候组件看起来的示例。

 

test

 

 

命名导出

如果你希望使用一个命名的导出组件,那么你需要再次导出他们,作为在独立的中间模块中的默认导出。

如果你有一个 OtherComponent 作为命名导出模块,你希望使用 React.lazy() 来加载 OtherComponent,那么你需要创建一个中间模块来再次导出 OtherComponent 作为 默认模块。

Component.js

export const FirstComponent = () => {/* 组件逻辑 */}

export const SecondComponent = () => {/* 组件逻辑 */}

export const OtherComponent = () => {/* 组件逻辑 */}
复制代码

OtherComponent.js

export { OtherComponet as defatul } from './Components';
复制代码

这时候你可以使用 React.lazy() 去加载 OtherComponent 从中间模块。

懒加载路由

使用 React.lazy()Suspense,现在很容易处理基于路由的代码分割而不使用其他外部依赖。你可以简单地转化应用的路由组建成为懒加载组件,包裹所有的路由通过 Suspense 组件。

下面的代码使用 React Router 展示了基于路由的代码分割:

import React, { Suspense } from 'react';
import { Router } from '@reach/router';
import Loading from './Loading';

const Home = React.lazy(() => import('./Home'));
const Dashboard = React.lazy(() => import('./Dashboard'));
const Overview = React.lazy(() => import('./Overview'));
const History = React.lazy(() => import('./History'));
const NotFound = React.lazy(() => import('./NotFound'));

function App() {
  return (
    <div>
      <Suspense fallback={<Loading />}>
        <Router>
          <Home path="/" />
          <Dashboard path="dashboard">
            <Overview path="/" />
            <History path="/history" />
          </Dashboard>
          <NotFound default />
        </Router>
      </Suspense>
    </div>
  )
}
复制代码

总结

With the new React.lazy()React.Suspense, code-splitting and lazy-loading React components has been made very easy.

现在开始从 React 16.6享受吧。

 

pic

 

 


作者:toddmark
链接:https://juejin.im/post/5c8efc41f265da681c1f1cb4
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。