react路由组件动态加载-优化首屏加载速度 - Go语言中文社区

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;
}
  1. asyncComponent函数以一个importComponent函数作为参数,importComponent用于动态调用一个组件。
  2. 在生命周期函数componentDidMount中,我们通过调用importComponent来引入函数,并存入到asyncComponent组件的state
  3. 最后,如果组件已完成加载,我们有条件地呈现该组件。在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>
    <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}
    />
    {/* Finally, catch all unmatched routes */}
    <Route component={AsyncNotFound} />
  </Switch>
;
  • 通过以上优化后,我们可以再次打包看看npm run build
  • 打包后可以看到:
    打包后文件
  • 以上任何.chunk.js文件都对应着一个动态import()导入的组件,当项目体量变量时,这样的优化将会更明显。

实际应用

  • 以上动态加载组件的函数asyncComponent实际已经有对应的成熟的库React.lazy。下面示例介绍如何使用React RouterReact.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 state
  if (isLoading) {
    return <div>Loading...</div>;
  }
  // Handle the error state
  else if (error) {
    return <div>Sorry, there was a problem loading the page.</div>;
  }
  else {
    return null;
  }
};
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Sophie_U/article/details/84645366
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢