作者:俊廷淑芳淑幸 | 来源:互联网 | 2023-06-19 00:17
通常情况下,使用create-react-app
进行打包后,会生成最终打包文件main.js
,且这个文件在项目内容变多,引用第三方插件后,但得很大(>100kb)。
通过Code Spliting
进行代码拆分,并使用动态import
使路由对应组件在使用时才被加载,可以优化打包,由原来一个main.js生成为多个单独的js
文件,并在首次加载时,只加载首屏用到的组件,从而提高首屏加载速度。
官方提供了React.lazy
来进行组件的动态导入,用以优化页面加载速度
动态import与React Router V4
【旧方式】通常我们在写React路由时,是以以下这种方式:
import Home from "./containers/Home" ;
import Posts from "./containers/Posts" ;
import NotFound from "./containers/NotFound" ;
export default ( ) =>
< Switch>
< Route path= "/" exact component= { Home} / >
< Route path= "/posts/:id" exact component= { Posts} / >
< Route component= { NotFound} / >
< / Switch> ;
Switch
用于渲染匹配当前路径的路由
以上,当我们将所有组件都在文件顶部引入时,意味着所有的组件都会被全部加载,而我们通过Code Spliting
可以实现只加载匹配当前路径的路由对应组件功能。
优化CodeSpliting
构建一个异步导入组件 Async Component
首先我们需要创建一个用于异步加载组件的函数:
添加如下文件src/components/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;
}
asyncComponent
函数以一个importComponent
函数作为参数,importComponent
用于动态调用一个组件。
在生命周期函数componentDidMount
中,我们通过调用importComponent
来引入函数,并存入到asyncComponent
组件的state
中
最后,如果组件已完成加载,我们有条件地呈现该组件。在render
中,我们除了可以简单使用return null
来应对未加载组件情况外,还可以使用一个loading spinner
加载动画组件来提升用户体验,避免白屏。
使用异步组件
原来引用组件的方式如下:
import Home from './containers/Home'
const AsyncHome = asyncComponent ( ( ) => import ( './containers/Home' ) )
注意:这里我们只是在AysncHome组件被创建时使用了一个函数来动态import()
,并没有直接同步的导入了Home组件。webpack
将会基于此做代码拆分。
< Route path= "/" exact component= { AsyncHome} / >
完整示例
import React from "react" ;
import { Route, Switch } from "react-router-dom" ;
import asyncComponent from "./components/AsyncComponent" ;
import AppliedRoute from "./components/AppliedRoute" ;
import AuthenticatedRoute from "./components/AuthenticatedRoute" ;
import UnauthenticatedRoute from "./components/UnauthenticatedRoute" ;
const AsyncHome = asyncComponent ( ( ) => import ( "./containers/Home" ) ) ;
const AsyncLogin = asyncComponent ( ( ) => import ( "./containers/Login" ) ) ;
const AsyncNotes = asyncComponent ( ( ) => import ( "./containers/Notes" ) ) ;
const AsyncSignup = asyncComponent ( ( ) => import ( "./containers/Signup" ) ) ;
const AsyncNewNote = asyncComponent ( ( ) => import ( "./containers/NewNote" ) ) ;
const AsyncNotFound = asyncComponent ( ( ) => import ( "./containers/NotFound" ) ) ;
export default ( { childProps } ) =>
< Switch>
< AppliedRoute
path= "/"
exact
component= { AsyncHome}
props= { childProps}
/ >
< UnauthenticatedRoute
path= "/login"
exact
component= { AsyncLogin}
props= { childProps}
/ >
< UnauthenticatedRoute
path= "/signup"
exact
component= { AsyncSignup}
props= { childProps}
/ >
< AuthenticatedRoute
path= "/notes/new"
exact
component= { AsyncNewNote}
props= { childProps}
/ >
< AuthenticatedRoute
path= "/notes/:id"
exact
component= { AsyncNotes}
props= { childProps}
/ >
{ }
< Route component= { AsyncNotFound} / >
< / Switch>
;
通过以上优化后,我们可以再次打包看看npm run build
打包后可以看到:
以上任何.chunk.js
文件都对应着一个动态import()
导入的组件,当项目体量变量时,这样的优化将会更明显。
实际应用
以上动态加载组件的函数asyncComponent
实际已经有对应的成熟的库React.lazy
。下面示例介绍如何使用React Router
和React.lazy
设置基于路由的代码拆分应用。
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' ;
import React, { Suspense, lazy } from 'react' ;
const Home = lazy ( ( ) => import ( './routes/Home' ) ) ;
const About = lazy ( ( ) => import ( './routes/About' ) ) ;
const App = ( ) => (
< Router>
< Suspense fallback= { < div> Loading... < / div> } >
< Switch>
< Route exact path= "/" component= { Home} / >
< Route path= "/about" component= { About} / >
< / Switch>
< / Suspense>
< / Router>
) ;
Suspense组件用于处理组件未加载完成时,显示loading的情况。fallback
接受任意React组件
写在最后
通过动态加载
可以很好的拆分代码,提升加载速度,而实现路由动态加载的关键在于:一个动态导入组件的函数
、对路由组件使用动态导入
给组件加载过程中加loading spinner
,当组件加载耗时长或者失败时,需要一个友好提示。
推荐使用react-loadable
,简单使用方式如下:
$ npm install --save react-loadable
const AsyncHome = Loadable ( {
loader: ( ) => import ( "./containers/Home" ) ,
loading: MyLoadingComponent
} ) ;
const MyLoadingComponent = ( { isLoading, error} ) => {
if ( isLoading) {
return < div> Loading... < / div> ;
}
else if ( error) {
return < div> Sorry, there was a problem loading the page. < / div> ;
}
else {
return null ;
}
} ;