利用懒加载(Lazy Loading)
优化页面性能不是什么新概念,不过React 16.6可使用React.lazy
与Suspense
让原生React实现Lazy Loading大大的简化。前端
本篇文章也会拓展多种相似解决方案,见拓展
部分react
动态导入
目前只是TC39的一个提案,不是JS(ES)标准的一部分。该功能能够动态化加载咱们分割的组件/页面/文件,具体提案见下方连接: TC39 proposal-dynamic-import提案webpack
目前要使用动态导入
,须要使用到babel插件:git
babel-plugin-syntax-dynamic-importgithub
lazy函数
提供了一种很是简便的方法动态导入组件,实现按需加载与代码分割web
点击查看文档性能优化
React.lazy使用方式很是简单,示例:babel
// lazy 接受一个函数做为参数
const MyComponent = React.lazy(() => import('path/MyComponent'));
// 展现时才加载(能够自定义一些条件)
const App = () => (
<div> <MyComponent /> </div>
)
复制代码
Suspense
组件用于包装lazy组件,在lazy组件尚未彻底加载时,将fallback内容呈现给用户。网络
用动态加载,编译时会将文件分割,从加载文件到呈现会有时间延迟,此时可使用Suspense
展现一个loading。
使用示例
import React, { Suspense } from 'react';
const MyComponent = React.lazy(() => import('path/MyComponent'));
const Loading = (
<h3>loading...</h3>
);
const App = () => (
<div> <Suspense fallback={Loading}> <MyComponent /> </Suspense> </div>
)
复制代码
ErrorBoundary(错误边界)
,可使用ErrorBoundary包装Suspense,当Suspense出错时,统一处理错误。固然ErrorBoundary
不止可用于Suspense,能够包装任意组件,而后统一处理错误。
实际场景中,能够本身先写一些通用错误页/组件继承自ErrorBoundary
,而后包装要处理的组件。
也能够在组件的componentDidCatch
生命周期中单独处理错误。
针对React加载的优化,能够分为两类:
很明显,React.lazy + Suspense能够很方便的应用于组件加载优化场景。
CSR(客户端渲染)应用
多页面的中某个耗时组件的加载
路由层
import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import Header from './header';
import Index from '../pages';
import Welcome from '../pages/welcome';
export default () => (
<Router>
<div>
<Header />
<Route exact path="/" component={Index}/>
<Route path="/welcome" component={Welcome}/>
</div>
</Router>
);
复制代码
某个存在耗时组件的页面
// welcome.js
import React, { Suspense } from 'react';
// 使用Suspense + React.lazy动态加载的耗时组件
const Content = React.lazy(() => import('../components/content'));
export default () => (
<div> <h1>Welcome</h1> <Suspense fallback={<div>loading...</div>}> <Content/> </Suspense> </div>
);
复制代码
假设用webpack编译上面代码产出bundle.js。当进入首页时,加载的bundle.js不会包含Welcome Content部分的代码,切换页面到Welcome时会走网络加载须要的文件。
利用这种方法拆分多个组件时,能够显著的提高首屏时间。
说明:该项目主要用于React一些新功能及有意思的用法示例,会持续更新,后面会涉及到hooks
及最新源码相关,有兴趣的能够star。
可使用 Next.js ,Next.js已支持直接使用dynamic import,而且能够指明何时动态导入(服务端/客户端),Next.js对首屏作了不少优化,首屏时,服务端会寻找到最简依赖渲染,具体能够点击前面连接查看文档。示例:
// Next.js 动态导入
const MyComponent = dynamic(import('../components/MyComponent'), {
ssr: false,
loading: () => false,
});
复制代码
Render Props + React hooks + Context API
统一处理请求,支持SSR使用封装react-request
示例(统一请求处理)
import React from 'react';
import PropTypes from 'prop-types';
import { Fetch } from 'react-request';
// import ReactLoading from 'react-loading';
import config from '../../config';
const CustomFetch = (props) => {
const { url, children } = props;
return (
<Fetch {...props} url={config.serverUrl + url} transformData={data => data.data} > {({ fetching, failed, data }) => { if (fetching) { // 正在请求:能够展现loading动画 // return <ReactLoading type="cylon" color="#2db53f" height="10%" width="10%" />; } if (failed) { // 当前组件请求失败 // return <h1 className="home">请求失败...</h1>; } if (data) { // 请求成功 return children(data); } return null; }} </Fetch> ); }; CustomFetch.propTypes = { url: PropTypes.string.isRequired, }; export default CustomFetch; 复制代码
在组件中使用CustomFetch
buildLinks = () => (
<div className="links"> <CustomFetch url="/getLinks" > {data => data.map((item, index) => ( <a className="item" href={item.url} key={`link-${index}`}> <img src={item.icon} className="icon" alt={item.name} /> <p>{item.name}</p> </a> ))} </CustomFetch> </div> ); 复制代码