React中有什么懒惰?

时间:2018-11-06 15:25:10

标签: javascript reactjs react-native

我正在阅读react官方文档,以了解关于react lazy的知识。

根据文档,https://reactjs.org/docs/code-splitting.html#reactlazy

  

呈现此组件时,它将自动加载包含OtherComponent的捆绑软件。

     

React.lazy使用必须调用动态import()的函数。这必须返回一个Promise,该解析为包含默认导出的包含React组件的模块。

     

正常导入

import OtherComponent from './OtherComponent';
function MyComponent() {
    return (
        <div>
           <OtherComponent />
        </div>
    );
}
     

惰性导入

const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
    return (
       <div>
           <OtherComponent />
      </div>
    );
}

但是我从文档中对使用惰性导入组件的优势了解得不多。还有为什么承诺会在导入模块/组件时成为现实?

5 个答案:

答案 0 :(得分:3)

单页应用程序(SPA)通常可以通过下载包含您应用所需的所有脚本的捆绑包来工作。浏览器地址栏中的路由和URL更改是通过HTML5 history API处理的。由于浏览器无需刷新来加载应用程序的任何部分,因此可创建强大的用户体验-您可以立即从一个页面/ URL导航到另一个页面/ URL。

虽然从理论上讲听起来不错,但实际上,情况却很丑陋。最大的问题之一是捆绑包的大小-在中等复杂度的SPA中,捆绑包的大小很容易达到兆字节。现在,如此大的文件不仅需要花费很长时间下载,而且也不会被浏览器缓存-因此需要一次又一次地获取它们,这反过来会使您的应用程序显得呆滞。

已经进行了许多尝试来解决此问题-asynchronous and deferred loading of scripts,并且进行了代码拆分。代码拆分是指将大型捆绑包拆分为较小的的技术-这种思想是您尽快下载应用程序的核心或关键部分,然后按需加载其余代码。这解决了上面提到的问题,但是实现代码拆分非常困难。

原因之一是,Webpack之类的工具很难通过较少的人工干预来弄清楚如何有效地拆分代码。在AMD的世界中,define/require帮助您定义了代码分割点-因此,您可以先加载关键部分,然后按需求以异步方式加载其余部分。

在React世界中,lazy让您以较少的人工干预有效地做到这一点。通过定义lazy,可以表明这部分代码是非关键的,可以根据需要在后台加载。

() => import('./OtherComponent')的语法也称为dynamic import,它与静态导入import OtherComponent from './OtherComponent'不同。动态导入是异步的,因此它们始终返回Promise。 承诺确保 依赖 的代码与此延迟加载的模块 执行 仅在脚本加载后。

请考虑以下代码段:

const PurchaseHistory = lazy(() => import('./components/PurchaseHistory'))

class App extends Component {
  state = {
    purchases: [ ],
  };

  componentDidMount() {
    fetch(`some/api/that/returns/data`)
      .then(res => res.json())
      .then(res => {
        this.setState({ purchases: res.data });
      });
  }
  render() {    
    return (
      <div className="app">
        <Suspense fallback={Loader}>
          <PurchaseHistory {...purchases} />
        </Suspense>
      </div>
    );
  }
}

在上面的代码中,React可以立即使用Loader渲染应用程序,直到获取成功为止,它甚至不需要加载惰性PurchaseHistory组件。这样可以节省下载时间,减少内存占用并节省下载和处理PurchaseHistory组件所需的CPU周期。

Suspense是React 16.6中的新API。

上面的代码与即将推出的另一个名为 Concurrent Rendering 的功能结合在一起,可确保React相当快地渲染应用程序(比React 16更快)。之所以可以这样做,是因为React知道哪些部分至关重要,哪些不重要。

为进一步阅读,我建议这个blog post讨论所有3个功能以及代码示例。

答案 1 :(得分:2)

它使您可以延迟加载其他组件的脚本。如果该脚本与其他脚本分开(例如,不属于您的主捆绑包),那么这将非常有用。

优点是:

  • 您的初始启动速度更快(假设您的组件不在初始显示中)
  • 如果用户从不执行显示您的组件的操作,则永远不需要加载其他组件的脚本(这样就保存了HTTP请求,并且该模块消耗了内存加载)

当然有缺点:

  • 当需要显示您的组件时,会因需求加载其他组件的脚本(HTTP请求,顶级模块评估)而延迟
  • 如果要捆绑在一起,则很复杂,现在您必须在捆绑软件中包含一些脚本,而捆绑软件中不包含其他脚本

与往常一样,权衡将对某些项目采取一种方式,而对其他项目则采取另一种方式。大型的单页面应用程序具有用户可能会或可能不会访问的许多部分,这可能会有所帮助;一个很小的页面,其中所有组件可能都在很早就呈现了。

答案 2 :(得分:1)

单页应用程序开始变大时。一次加载所有javascript没有意义。假设您在首页上,并且为“个人资料”部分加载javascript毫无意义,这会减慢首页的页面加载速度。因此,将大型应用程序代码拆分为较大的块将使应用程序性能更好。简而言之lazy loading您的组件。

答案 3 :(得分:1)

基本上,对于常规导入,它将导入父组件所需的所有子组件。但是,对于延迟加载的子组件,它将异步加载它们。当子组件本身包含许多其他子组件层次结构时,这确实有助于节省父组件的初始加载时间,并最终延长了加载主页面的时间。但是,当对组件使用Lazyloading时,要延迟加载的组件必须使用带有回退属性的“ Suspense”标签封闭,因为当父组件加载并且提供了jsx时,延迟加载的组件的内容将不可用然后将加载fallback属性中的

答案 4 :(得分:1)

简单地说,如果不使用延迟加载的任何组件,则不会在浏览器中下载它。

在下面的代码中,导入了HelloBye。 但是,由于仅使用Hello,因此不包含Bye的代码。

import React, { lazy, Suspense } from "react";
import ReactDOM from "react-dom";

const Hello = lazy(() => import("./components/Hello"));
const Bye = lazy(() => import("./components/Hello"));

function App() {
  return (
    <Suspense fallback={<>Loading...</>}>
      <Hello />
    </Suspense>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

您可以继续关注CodeSandbox
Edit so.answer.53174915

在Netlify上检查deployed site时,
您可以从站点上看到Bye尚未加载。

enter image description here


您可以通过按需加载组件来利用此优势。

无耻的插件:如果您想进一步了解其他用例,请查看我的博客条目Loading React Components Dynamically on Demand using React.lazy