react路由组件动态加载-优化首屏加载速度
通常情况下,使用
create-react-app进行打包后,会生成最终打包文件main.js,且这个文件在项目内容变多,引用第三方插件后,但得很大(>100kb)。
- 通过
Code Spliting进行代码拆分,并使用动态import使路由对应组件在使用时才被加载,可以优化打包,由原来一个main.js生成为多个单独的js文件,并在首次加载时,只加载首屏用到的组件,从而提高首屏加载速度。 - 官方提供了
React.lazy来进行组件的动态导入,用以优化页面加载速度
动态import与React Router V4
- 【旧方式】通常我们在写React路由时,是以以下这种方式:
/* Import the components */
import Home from "./containers/Home";
import Posts from "./containers/Posts";
import NotFound from "./containers/NotFound";/* Use components to define routes */
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;
}
- 关于
async-await的理解可以参考这篇文章:异步神器async-await
以上代码做了如下几件事:
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} />
完整示例
src/Routes.js
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><AppliedRoutepath="/"exactcomponent={AsyncHome}props={childProps}/><UnauthenticatedRoutepath="/login"exactcomponent={AsyncLogin}props={childProps}/><UnauthenticatedRoutepath="/signup"exactcomponent={AsyncSignup}props={childProps}/><AuthenticatedRoutepath="/notes/new"exactcomponent={AsyncNewNote}props={childProps}/><AuthenticatedRoutepath="/notes/:id"exactcomponent={AsyncNotes}props={childProps}/>{/* Finally, catch all unmatched routes */}<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
});
// MyLoadingComponent长这样:
const MyLoadingComponent = ({isLoading, error}) => {// Handle the loading stateif (isLoading) {return <div>Loading...</div>;}// Handle the error stateelse if (error) {return <div>Sorry, there was a problem loading the page.</div>;}else {return null;}
};
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
