react新版本的lazy和suspense实现组件懒加载和路由懒加载
日期:2019-11-01
来源:程序思维浏览:3841次
react16.6之前的版本没有路由懒加载需要自己手动实现,现在有了lazy和suspense可以轻松的做到路由懒加载现在咱们看看怎么实现吧:
咱们先来看看不用lazy和suspense如何实现路由懒加载:
1、建立AsyncComponent.js文件里面的代码:
import React, { Component } from "react";
export default function asyncComponent(importComponent) {
class AsyncComponent extends Component {
constructor(props) {
super(props);
this.state = {
component: null
};
}
async componentDidMount() {
const { default: component } = await importComponent();
this.setState({
component: component
});
}
render() {
const C = this.state.component;
return C ? <C {...this.props} /> : null;
}
}
return AsyncComponent;
}
2、在router.js路由配置文件里面写入以下代码:
import asyncComponent from './components/async/AsyncComponent';
const IndexPage=asyncComponent(()=>import("./pages/index/index"));//用aysncComponent来包裹组件,实现路由懒加载
<Route path="/" exact component={IndexPage}></Route>
好了以上方案的好处是不需要考虑兼容性问题,16版本之后都兼容。
下面来看看lazy和suspense来实现路由懒加载吧:
组件懒加载
本演示将使用react-pdf。react-pdf是一个很棒的库,用于在浏览器、移动端和服务器上创建PDF文件。一般我们都在服务器上生成PDF,但是如果我们需要在客户端进行,则需要一个成本:包大小。
首先,假设我们的需求是在用户点击按钮时生成PDF文件。
其次,如果这是一个很大的web app以及这个功能可能是很小一部分,也许是用户不经常使用的功能。每次页面请求都会加载整个react-pdf代码就变得无意义了。
所以,我们非常需要一个延迟加载的解决方案。
预加载 VS 懒加载 展示
一个简易的PDFPreview组件
import React from "react";
import { PDFViewer, Document, Page, Text, View } from "@react-pdf/renderer";
import pdfstyles from "./pdfStyles";
// Create Document Component
const PDFPreview = ({ title }) => (
<PDFViewer className="viewer" style={pdfstyles.viewer}>
<Document>
<Page size="A4" style={pdfstyles.page}>
<View style={pdfstyles.section}>
<Text style={pdfstyles.title}>{title}</Text>
<Text>This is a text in a generated PDF file.</Text>
</View>
</Page>
</Document>
</PDFViewer>
);
export default PDFPreview;
添加样式
import { StyleSheet } from "@react-pdf/renderer";
const styles = StyleSheet.create({
viewer: {
padding: 0
},
page: {
margin: 0,
flexDirection: "row",
backgroundColor: "#ffffff"
},
title: {
fontSize: 30,
marginBottom: 30
},
section: {
margin: 10,
padding: 10,
flexGrow: 1
}
});
export default styles;
预加载
让我们看看没有延迟加载的父组件
import React, { Component } from "react";
import PDFPreview from "./PDFPreview";
class App extends Component {
state = {
name: "",
showPDFPreview: false
};
handleClick = () => this.setState({ showPDFPreview: true });
handleNameChange = event => this.setState({ name: event.target.value });
render() {
const greeting = `Hello ${this.state.name}`;
return (
<div className="App">
<input
placeholder="Enter your name"
type="text"
onChange={this.handleNameChange}
/>
<button onClick={this.handleClick}>Generate PDF</button>
{this.state.showPDFPreview && <PDFPreview title={greeting} />}
</div>
);
}
}
export default App;
在浏览器中将呈现以下视图
无论我们是否点击生成PDF,与其相关的所有代码都包含在应用包内了
这是一个开发环境。在打包之后,Size将显著缩小。尽管如此,我们还是没有最佳地分割代码。
懒加载
import React, { Component, Suspense } from "react";
const LazyPDFDocument = React.lazy(() => import("./PDFPreview"));
class App extends Component {
state = {
name: "",
showPDFPreview: false
};
handleClick = () => this.setState({ showPDFPreview: true });
handleNameChange = event => this.setState({ name: event.target.value });
render() {
const greeting = `Hello ${this.state.name}`;
return (
<div className="App">
<input
placeholder="Enter your name"
type="text"
onChange={this.handleNameChange}
/>
<button onClick={this.handleClick}>Generate PDF</button>
{this.state.showPDFPreview && (
<Suspense fallback={<div>Loading...</div>}>
<LazyPDFDocument title={greeting} />
</Suspense>
)}
</div>
);
}
}
export default App;
我们只做了很小的改动:
第2行替换为:
const LazyPDFDocument = React.lazy(() => import("./PDFPreview"));
让我们看看React文档对React.lazy的说法:
React.lazy必须通过调用动态的import()加载一个函数,此时会返回一个Promise,并解析(resolve)为一个带有包含React组件的默认导出的模块。 - reactjs.org
因此如果的IE中使用,需要预先使用PolyfillPromise来实现动态import
在第27行,我们使用Suspense,它必须是延迟加载组件的父级(即React.lazy加载的组件只能在<React.Suspense>组件下使用)。当showPDFPreview设置为true时,LazyPDFDocument开始加载。
在子组件被解析之前,Suspense会显示由fallback属性提供的任何内容,且fallback为必带参数
路由懒加载
比如:router.js路由配置文件里面的代码:
import React,{lazy,Suspense} from 'react';
import {HashRouter as Router,Route} from 'react-router-dom';
const IndexPage=lazy(()=>import("./pages/index")); //使用lazy包裹组件,实现路由懒加载
const NewsPage=lazy(()=>import("./pages/news"));
const NewsDetailsPage=lazy(()=>import("./pages/news/details"));
class RouterComponent extends React.Component{
render(){
return (
<React.Fragment>
<Router>
<React.Fragment>
<Suspense fallback={<React.Fragment/>}> //使用Suspense包裹路由组件
<Route path="/" exact component={IndexPage}></Route>
<Route path="/news" exact component={NewsPage}></Route>
{/*<Route path="/news/details/:id/:title" component={NewsDetailsPage}></Route>*/}
<Route path="/news/details" component={NewsDetailsPage}></Route>
</Suspense>
</React.Fragment>
</Router>
</React.Fragment>
)
}
}
export default RouterComponent;
好了这样就能实现路由懒加载了。
咱们先来看看不用lazy和suspense如何实现路由懒加载:
1、建立AsyncComponent.js文件里面的代码:
import React, { Component } from "react";
export default function asyncComponent(importComponent) {
class AsyncComponent extends Component {
constructor(props) {
super(props);
this.state = {
component: null
};
}
async componentDidMount() {
const { default: component } = await importComponent();
this.setState({
component: component
});
}
render() {
const C = this.state.component;
return C ? <C {...this.props} /> : null;
}
}
return AsyncComponent;
}
2、在router.js路由配置文件里面写入以下代码:
import asyncComponent from './components/async/AsyncComponent';
const IndexPage=asyncComponent(()=>import("./pages/index/index"));//用aysncComponent来包裹组件,实现路由懒加载
<Route path="/" exact component={IndexPage}></Route>
好了以上方案的好处是不需要考虑兼容性问题,16版本之后都兼容。
下面来看看lazy和suspense来实现路由懒加载吧:
组件懒加载
本演示将使用react-pdf。react-pdf是一个很棒的库,用于在浏览器、移动端和服务器上创建PDF文件。一般我们都在服务器上生成PDF,但是如果我们需要在客户端进行,则需要一个成本:包大小。
首先,假设我们的需求是在用户点击按钮时生成PDF文件。
其次,如果这是一个很大的web app以及这个功能可能是很小一部分,也许是用户不经常使用的功能。每次页面请求都会加载整个react-pdf代码就变得无意义了。
所以,我们非常需要一个延迟加载的解决方案。
预加载 VS 懒加载 展示
一个简易的PDFPreview组件
import React from "react";
import { PDFViewer, Document, Page, Text, View } from "@react-pdf/renderer";
import pdfstyles from "./pdfStyles";
// Create Document Component
const PDFPreview = ({ title }) => (
<PDFViewer className="viewer" style={pdfstyles.viewer}>
<Document>
<Page size="A4" style={pdfstyles.page}>
<View style={pdfstyles.section}>
<Text style={pdfstyles.title}>{title}</Text>
<Text>This is a text in a generated PDF file.</Text>
</View>
</Page>
</Document>
</PDFViewer>
);
export default PDFPreview;
添加样式
import { StyleSheet } from "@react-pdf/renderer";
const styles = StyleSheet.create({
viewer: {
padding: 0
},
page: {
margin: 0,
flexDirection: "row",
backgroundColor: "#ffffff"
},
title: {
fontSize: 30,
marginBottom: 30
},
section: {
margin: 10,
padding: 10,
flexGrow: 1
}
});
export default styles;
预加载
让我们看看没有延迟加载的父组件
import React, { Component } from "react";
import PDFPreview from "./PDFPreview";
class App extends Component {
state = {
name: "",
showPDFPreview: false
};
handleClick = () => this.setState({ showPDFPreview: true });
handleNameChange = event => this.setState({ name: event.target.value });
render() {
const greeting = `Hello ${this.state.name}`;
return (
<div className="App">
<input
placeholder="Enter your name"
type="text"
onChange={this.handleNameChange}
/>
<button onClick={this.handleClick}>Generate PDF</button>
{this.state.showPDFPreview && <PDFPreview title={greeting} />}
</div>
);
}
}
export default App;
在浏览器中将呈现以下视图
无论我们是否点击生成PDF,与其相关的所有代码都包含在应用包内了
这是一个开发环境。在打包之后,Size将显著缩小。尽管如此,我们还是没有最佳地分割代码。
懒加载
import React, { Component, Suspense } from "react";
const LazyPDFDocument = React.lazy(() => import("./PDFPreview"));
class App extends Component {
state = {
name: "",
showPDFPreview: false
};
handleClick = () => this.setState({ showPDFPreview: true });
handleNameChange = event => this.setState({ name: event.target.value });
render() {
const greeting = `Hello ${this.state.name}`;
return (
<div className="App">
<input
placeholder="Enter your name"
type="text"
onChange={this.handleNameChange}
/>
<button onClick={this.handleClick}>Generate PDF</button>
{this.state.showPDFPreview && (
<Suspense fallback={<div>Loading...</div>}>
<LazyPDFDocument title={greeting} />
</Suspense>
)}
</div>
);
}
}
export default App;
我们只做了很小的改动:
第2行替换为:
const LazyPDFDocument = React.lazy(() => import("./PDFPreview"));
让我们看看React文档对React.lazy的说法:
React.lazy必须通过调用动态的import()加载一个函数,此时会返回一个Promise,并解析(resolve)为一个带有包含React组件的默认导出的模块。 - reactjs.org
因此如果的IE中使用,需要预先使用PolyfillPromise来实现动态import
在第27行,我们使用Suspense,它必须是延迟加载组件的父级(即React.lazy加载的组件只能在<React.Suspense>组件下使用)。当showPDFPreview设置为true时,LazyPDFDocument开始加载。
在子组件被解析之前,Suspense会显示由fallback属性提供的任何内容,且fallback为必带参数
路由懒加载
比如:router.js路由配置文件里面的代码:
import React,{lazy,Suspense} from 'react';
import {HashRouter as Router,Route} from 'react-router-dom';
const IndexPage=lazy(()=>import("./pages/index")); //使用lazy包裹组件,实现路由懒加载
const NewsPage=lazy(()=>import("./pages/news"));
const NewsDetailsPage=lazy(()=>import("./pages/news/details"));
class RouterComponent extends React.Component{
render(){
return (
<React.Fragment>
<Router>
<React.Fragment>
<Suspense fallback={<React.Fragment/>}> //使用Suspense包裹路由组件
<Route path="/" exact component={IndexPage}></Route>
<Route path="/news" exact component={NewsPage}></Route>
{/*<Route path="/news/details/:id/:title" component={NewsDetailsPage}></Route>*/}
<Route path="/news/details" component={NewsDetailsPage}></Route>
</Suspense>
</React.Fragment>
</Router>
</React.Fragment>
)
}
}
export default RouterComponent;
好了这样就能实现路由懒加载了。
精品好课