内容简介:React 16.6添加了一个新的特性: React.lazy(), 它可以让代码分割(code splitting)更加容易。接下来通过一个股票App Demo, 来学习如何使用React.lazy这个新特性并了解为什么要使用它。
译: Lazy loading (and preloading) components in React 16.6
React 16.6添加了一个新的特性: React.lazy(), 它可以让代码分割(code splitting)更加容易。
接下来通过一个股票App Demo, 来学习如何使用React.lazy这个新特性并了解为什么要使用它。
我们创建了一个股票Web App,App展示了一些股票的列表,点击其中的一个股票,它会展示出最近这只股票的走势图。
try it以上就是App的全部功能了。 你可以在 Github Repo 阅读项目源码(也可以通过PR,查看每一次提交的项目变更和可运行版本。)
在本文,我们只关心 App.js
这个文件内的代码逻辑。
import React from "react"; import StockTable from "./StockTable"; import StockChart from "./StockChart"; class App extends React.Component { state = { selectedStock: null }; render() { const { stocks } = this.props; const { selectedStock } = this.state; return ( <React.Fragment> <StockTable stocks={stocks} onSelect={selectedStock => this.setState({ selectedStock })} /> {selectedStock && ( <StockChart stock={selectedStock} onClose={() => this.setState({ selectedStock: false })} /> )} </React.Fragment> ); } } export default App; 复制代码
App
组件获取股票列表的数据并且展示了 <StockTable/>
组件。当其中一个股票被点击选中时, App
将会展示那只股票的走势图 <StockChart>
这里有什么问题呢?
我们想要 App
尽可能快速加载与展示 <StockTable />
, 但 App
却要等待浏览器下载(解压, 分析, 编译, 执行等) StockChart
的代码。
通过Chrome DevTools可以看到展示 <StockTable />
所消耗的时间记录。
展示 StockTable
一共耗时2470ms(模拟Fast3G网络环境与4核普通CPU)
通过下图,可以了解到在向浏览器传输压缩后的125K文件中都包含了什么
如我们所预期,页面加载了react, react-dom 和一些react的依赖包,但页面也同时加载了组件依赖的moment, lodash, victory的依赖。展示 <StockTable />
是不需要这些依赖的。
那如何加载 <StockChart / >的依赖才不会影响的加载速度呢?
懒加载组件
通过使用webpack的'dynamic import', 我们可以将打包的代码拆分成两部分, main
文件里包含了需要展示 <StockTable>
的代码及依赖。另一个文件包含了展示 <StockChart />
的代码及依赖包。
dynamic import
技术是十分有用的,所以React16.6版本新添加了一个API - React.lazy()
, 可以更便利地去异步引用React组件。
为了在App.js中使用 React.lazy()
, 我们在代码中做了两处变更。
首先,将静态引用组件的代码 import StockChart from "./StockChart"
替换为调用 React.lazy()
,在 lazy()
传入一个匿名函数作为参数,在函数中动态引入 StockChart
组件。这样在我们渲染这个组件前,浏览器将不会下载 ./StockChart.js
文件和它的依赖。
如果React要渲染 <StockChart />
组件时,组件依赖的代码还没下载好,会怎样呢? 这就是为什么我们添加了 <React.Suspense/>
。在代码未下载好前,它将会渲染 fallback
props属性传入的值,当全部子节点依赖的代码都准备好后,才会去渲染子节点内容。
现在 App
将会被打包成两个文件。
main.js
文件只有36kb,包含 <StockChart />
及其依赖的代码文件89KB。
在优化后, 如下图,浏览器展示了 <StockTable />
需要消耗的时间。
!
浏览器用了760ms去下载 main.js
(以前是1250ms)和执行脚本消耗61ms(以前是487ms). 展示 <StockTable />
只用了1546ms(以前是2460ms)。
预加载-懒加载组件
现在我们已经让App加载的更快了。但还有另一个问题。
用户在第一次点击Item时,会展示"Loading...."的回退方案的组件。这是因为 App
需要等待浏览器加载好 <StockChart />
的代码。
如果我们想避免展示"Loading...."这样的loading状态,我们需要在用户点击之前就加载好代码。
一个简单实现预加载代码的方式就是提前调用React.lazy()
const stockChartPromise = import("./StockChart"); const StockChart = React.lazy(() => stockChartPromise); 复制代码
当我们调用 dynamic imoprt
时,组件就会开始加载,并且它不会阻塞 <StockTable />
组件的加载。
看下App加载的记录以及与未修改版本的对比
当用户在1s内点击Item时,才会看到“Loading...”
你也可以使用你自己的方式去优化lazy函数,让预加载组件更加通用方便。
function lazyWithPreload(factory) { const Component = React.lazy(factory); Component.preload = factory; return Component; } const StockChart = lazyWithPreload(() => import("./StockChart")); // somewhere in your component ... handleYouMayNeedToRenderStockChartSoonEvent() { StockChart.preload(); } ... 复制代码
预渲染组件
以上功能已经满足Demo App的使用了。但对于更大型的项目,在懒加载组件被加载之前,组件可能还会有其他懒加载组件的代码或数据,所以用户还是需要时间等待组件加载。
那另外一种预加载组件的方式就是提前渲染它。在页面中渲染组件,但是并不在页面中展示,也就是隐藏渲染。
class App extends React.Component { state = { selectedStock: null }; render() { const { stocks } = this.props; const { selectedStock } = this.state; return ( <React.Suspense fallback={<div>Loading...</div>}> <StockTable stocks={stocks} onSelect={selectedStock => this.setState({ selectedStock })} /> {selectedStock && ( <StockChart stock={selectedStock} onClose={() => this.setState({ selectedStock: false })} /> )} {/* Preload <StockChart/> */} <React.Suspense fallback={null}> <div hidden={true}> <StockChart stock={stocks[0]} /> </div> </React.Suspense> </React.Suspense> ); } } 复制代码
在App第一次渲染后,React将会加载 <Stockchart />
并尝试去渲染组件,所以组件需要的依赖或代码也会被加载。
我们将 懒加载组件
包裹在一个隐藏的div中, 在加载之后页面不会展示任何东西。并且还用了 React.suspense
包裹住这个 div
,且其 fallback
值为 null
,这样它在加载时也不会被展示出来。
注: hidden
属性通常表明该节点是不相关的,浏览器将不会渲染具有这个属性的元素。而React并不会对这个属性做任何特殊处理(但在未来的版本中可能会较低优先级处理被隐藏的组件)
还有什么呢?
最后一个方法在很多场景下是可用的,但它仍有一些问题。
第一, 对于被隐藏渲染的懒加载组件, hidden
属性并不是完全有效的。举个例子,使用portal的懒加载组件将不会被隐藏(可以使用portal来实现隐藏功能且不用一个额外的div,但这只是一个hack方式,在未来它将不会被使用)。
第二,虽然Dom节点已经被隐藏,但还是添加了额外的Dom节点,这可能会成为一个性能问题。
一个更好的办法就是,通知react去渲染这个懒加载组件,但在加载后并不把它添加到Dom树中。但据我所了解,在当前版本的React中是无法实现的。
另外我们能做的改进就是重复使用我们预渲染时准备的Dom,这样当真正要去渲染图表组件时,React就无需再创建一遍它。如果用户将会点击某只股票,我们可以在用户点击前用正确的数据渲染它( 就像这样 )
这就是文章的全部了,感谢阅读。
《IVWEB 技术周刊》震撼上线了,关注公众号:IVWEB社区,每周定时推送优质文章。
- 周刊文章集合: weekly
以上所述就是小编给大家介绍的《[译] React 16.6 懒加载(与预加载)组件》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
与孩子一起学编程
[美] 桑德Warren Sande、Carter Sande / 苏金国、姚曜 等 / 人民邮电出版社 / 2010-11 / 65.00元
一本老少咸宜的编程入门奇书!一册在手,你完全可以带着自己的孩子,跟随Sande父子组合在轻松的氛围中熟悉那些编程概念,如内存、循环、输入和输出、数据结构和图形用户界面等。这些知识一点儿也不高深,听起来备感亲切,书中言语幽默风趣而不失真义,让学习过程充满乐趣。细心的作者还配上了孩子们都喜欢的可爱漫画和经过运行测试的程序示例,教你用最易编写和最易理解的Python语言,写出你梦想中的游戏程序。 ......一起来看看 《与孩子一起学编程》 这本书的介绍吧!
HTML 编码/解码
HTML 编码/解码
RGB HSV 转换
RGB HSV 互转工具