代码拆分是将一个包含entireapp的大捆绑包拆分为多个包含应用程序单独部分的小捆绑包的过程。
这似乎很难做到,但像Webpack这样的工具都内置了这个功能,andReact Loadable的设计目的是让它变得超级简单。
基于路由的拆分与基于组件的拆分
一个常见的建议是将你的应用程序分成不同的路由,并异步加载每个路由。对于许多应用程序来说,这似乎已经足够好用了——作为一个用户,点击一个链接并等待页面加载是一种熟悉的web体验。
但我们可以做得更好
使用大多数路由工具进行React时,路由只是一个组件。他们没有什么特别的地方。所以,如果我们优化了围绕组件拆分而不是路由呢?那会给我们带来什么?
事实证明:相当多。有很多地方不仅仅是路线,你可以很容易地将你的应用程序分开。modal、tabs和更多的UIcomponents会隐藏内容,直到用户做了一些事情来显示内容。
由于路由只是组件,所以我们仍然可以在路由级别轻松地进行代码拆分。
引入React Loadable
React Loadable是一个小库,它使以组件为中心的代码拆分在React中非常容易。
Loadable
是一个高阶组件(创建组件的函数),它允许您在将任何模块呈现到应用程序之前动态加载它。
让我们设想两个组件,一个导入并呈现另一个。
import Bar from './components/Bar'; class Foo extends React.Component { render() { return <Bar/>; } }
现在我们依赖Bar
通过同步导入import
,但在渲染之前我们不需要它。那我们为什么不推迟呢?
使用动态导入我们可以修改组件以加载Bar
异步地
class MyComponent extends React.Component { state = { Bar: null }; componentWillMount() { import('./components/Bar').then(Bar => { this.setState({ Bar: Bar.default }); }); } render() { let {Bar} = this.state; if (!Bar) { return <div>Loading...</div>; } else { return <Bar/>; }; } }
但那是一大堆工作,甚至不能处理很多案件。什么时候呢import()
失败?服务器端渲染呢?
相反,您可以使用Loadable
把问题抽象化
import Loadable from 'react-loadable'; const LoadableBar = Loadable({ loader: () => import('./components/Bar'), loading() { return <div>Loading...</div> } }); class MyComponent extends React.Component { render() { return <LoadableBar/>; } }
自动代码拆分打开import()
当你使用import()
有了webpack2,它将自动代码拆分不需要额外的配置
这意味着您只需切换到import()
并使用React Loadable。找出你的应用程序的最佳性能。
创建一个很棒的“"Loading…”组件
呈现静态“"Loading…”与用户的通信不够。您还需要考虑错误状态、超时,并使之成为一种良好的体验。
function Loading() { return <div>Loading...</div>; } Loadable({ loader: () => import('./WillFailToLoad'), // oh no! loading: Loading, });
为了让这一切变得美好,你的loading component接受不同的道具
加载错误状态
当你loader失败了,你的loading component将收到error将成为错误
对象(否则null
).
function Loading(props) { if (props.error) { return <div>Error! <button onClick={ props.retry }>Retry</button></div>; } else { return <div>Loading...</div>; } }
回避加载元件闪光
Sometimes components load really quickly (<200ms) and the loading screen onlyquickly flashes on the screen.
大量的用户研究已经证明,这会导致用户感知事物的时间比实际时间长。如果你什么都不显示,用户会觉得速度更快。
所以你的加载组件也会得到一个pastDelay
道具只有当组件的加载时间比集合长时,才会出现这种情况延迟 .
function Loading(props) { if (props.error) { return <div>Error! <button onClick={ props.retry }>Retry</button></div>; } else if (props.pastDelay) { return <div>Loading...</div>; } else { return null; } }
此延迟默认为200ms
但您也可以自定义延迟在里面Loadable
.
Loadable({ loader: () => import('./components/Bar'), loading: Loading, delay: 300, // 0.3 seconds });
当时间到了loader
花费的时间太长了
有时网络连接很糟糕,从不解决或失败,它们只是永远挂在那里。这对用户来说太糟糕了,因为他们不知道是应该一直花这么长时间,还是应该尝试刷新。
这个正在加载组件将收到timedOut
道具将设置为是的
当loader
已超时
function Loading(props) { if (props.error) { return <div>Error! <button onClick={ props.retry }>Retry</button></div>; } else if (props.timedOut) { return <div>Taking a long time... <button onClick={ props.retry }>Retry</button></div>; } else if (props.pastDelay) { return <div>Loading...</div>; } else { return null; } }
但是,此功能在默认情况下处于禁用状态。要打开它,你可以通过timeout
选项到可装载
.
Loadable({ loader: () => import('./components/Bar'), loading: Loading, timeout: 10000, // 10 seconds });