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;
  }
};
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React 路由懒加载是一种优化技术,它可以帮助我们提高 React 应用程序的性能。当应用程序包含大量页面或组件时,使用路由懒加载可以减少初始加载时间并降低资源占用。 要实现路由懒加载,我们可以使用 React Router 库提供的 `React.lazy` 函数和 `Suspense` 组件。下面是一个示例: ```jsx import React, { lazy, Suspense } from 'react'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; // 使用 React.lazy 定义需要懒加载组件 const Home = lazy(() => import('./Home')); const About = lazy(() => import('./About')); const Contact = lazy(() => import('./Contact')); function App() { return ( <Router> <Suspense fallback={<div>Loading...</div>}> <Switch> <Route exact path="/" component={Home} /> <Route path="/about" component={About} /> <Route path="/contact" component={Contact} /> </Switch> </Suspense> </Router> ); } export default App; ``` 在上面的示例中,我们首先使用 `React.lazy` 函数将要懒加载组件引入。然后,我们将这些懒加载组件用 `<Suspense>` 组件包裹,并设置一个 `fallback` 属性来定义在组件加载过程中显示的加载提示。最后,我们在 `<Switch>` 组件中使用 `<Route>` 来定义各个页面的路由路径和对应的组件。 使用路由懒加载后,当用户访问到某个页面时,对应的组件才会被动态加载渲染,从而提高了应用程序的加载速度和性能。 希望对你有所帮助!如果还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值