react揚帆起航之路由配置與組件分割

路由

react-router v4 發生了巨大改變,由靜態路由開始轉向動態路由,今後,就像使用普通組件同樣來聲明路由。其實,路由今後就是一個普通組件。react

路由的按需加載的實質是代碼分割,react-router官網有個代碼拆分的示例是基於bundle-loader實現的.如今官網的代碼已經改成基於react-loadable實現。webpack

除此以外,咱們還能夠經過dynamic import來實現代碼分割,這裏也是本文使用的方式。git

首先,在項目根目錄建立AsyncComponent.tsx:github

// AsyncComponent.tsx
import * as React from 'react';

interface State {
  component: any;
}

const asyncComponent = (importComponent: any) => {

  class AsyncComponent extends React.Component<{}, State> {

    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} />
        : <div>loading...</div>;
    }

  }

  return AsyncComponent;
};

export default asyncComponent;

而後,將須要分割的組件經過dynamic import引入後做爲參數傳遞給 asyncComponent:web

// App.tsx
import * as React from 'react';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
import asyncComponent from './AsyncComponent';
import HeaderComponent from './Header';

const App = () => {
  const AsyncHome = asyncComponent(() => import('./Home/Home'));
  const AsyncNotFound = asyncComponent(() => import('./NotFound'));

  return (
    <Router>
      <div>
        <HeaderComponent />
        <div>
          <Switch>
            <Route exact={true} path="/">
              <Redirect
                to={{
                  pathname: '/home'
                }}
              />
            </Route>
            <Route path="/home" component={AsyncHome} />
            <Route path="*" component={AsyncNotFound} />
          </Switch>
        </div>
      </div>
    </Router>
  );
};

export default App;

至此,咱們完成了路由的按需加載。segmentfault

基於react-loadable實現代碼分割

按照上述方法,已經實現了代碼分割。然而,react-loadable爲咱們提供了更好的解決方案。react-loadable核心實現原理也是經過dynamic import來實現組件的異步加載。在此基礎上,它提供了更爲強大的功能,如根據延遲時間肯定是否顯示loading組件、預加載、支持服務端渲染(關於與服務端渲染的搭配會在後續的文章中提到)等。react-router

OK,如今將上述的App.tsx改造以下:dom

// App.tsx
import * as React from 'react';
import { Route, Redirect, Switch } from 'react-router-dom';
import * as Loadable from 'react-loadable';
import HeaderComponent from './Header';
import LoadingComponent from './Loading';

interface Props {
  pathname: string;
}

const AsyncHome = Loadable({
  loader: () => import(/* webpackChunkName: "homeChunk" */'./Home/Home'),
  loading: LoadingComponent,
  modules: ['homeChunk'],
  // home組件比設定的delay更長的時間加載的時候 => loading
  delay: 200, // 200ms
  // home組件比設定的timeout更長的時間加載的時候 => error
  timeout: 10000 // 10s
});

const AsyncNotFound = Loadable({
  loader: () => import(/* webpackChunkName: "notFoundChunk" */'./NotFound'),
  loading: LoadingComponent,
  modules: ['notFoundChunk'],
  delay: 200, // 200ms
  timeout: 10000 // 10s
});

const App = () => {

  return (
    <div>
      <HeaderComponent />
      <div>
        <Switch>
          <Route exact={true} path="/">
            <Redirect
              to={{
                pathname: '/home'
              }}
            />
          </Route>
          <Route path="/home" component={AsyncHome} />
          <Route path="*" component={AsyncNotFound} />
        </Switch>
      </div>
    </div>
  );
};

export default App;

咱們的loading組件以下:異步

//Loading.tsx
import * as React from 'react';

const LoadingComponent = (props: { [key: string]: string | any }) => {
  if (props.error) {
    return <h1>Error!</h1>;
  } else if (props.timedOut) {
    return <h1>Taking a long time...</h1>;
  } else if (props.pastDelay) {
    return <h1>Loading...</h1>;
  } else {
    return null;
  }
};

export default LoadingComponent;

關於react-loadable的預加載等功能的使用這裏再也不介紹,它的github頁面提供了詳細的介紹,有興趣的讀者能夠自行了解下。async

至此,路由配置與組件分割的內容完畢。


react揚帆啓航專欄分享的時我我的學習與實踐react過程當中的一些歷程,但願藉此專欄與你們共同探討react相關的技術,以求進步。

相關文章
相關標籤/搜索