React 異步組件

以前寫過一篇 Vue 異步組件的文章,最近在作一個簡單項目的時候又想用到 React 異步組件,因此簡單地瞭解了一下使用方法,這裏作下筆記。html

傳統的 React 異步組件基本都靠本身實現,本身寫一個專門的 React 組件加載函數做爲異步組件的實現工具,經過 import() 動態導入,實現異步加載,能夠參考【翻譯】基於 Create React App路由4.0的異步組件加載(Code Splitting)這篇文章。這樣作的話仍是要本身寫一個單獨的加載組件,有點麻煩。因而想找個更簡單一點的方式,沒想到真給找到了:Async React using React Router & Suspense,這篇文章講述瞭如何基於 React Router 4 和 React 的新特性快速實現異步組件按需加載。react

2018 年 10 月 23 號,React 發佈了 v16.6 版本,新版本中有個新特性叫 lazy,經過 lazy 和 Suspense 組件咱們就能夠實現異步組件,若是你使用的是 React v16.6 以上版本:git

最簡單的實現方法:github

// codes from https://react.docschina.org

import React, { lazy, Suspense } from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <OtherComponent />
    </Suspense>
  );
}

從 React 中引入 lazy 方法和 Suspense 組件,而後用 lazy 方法處理咱們的組件,lazy 會返回一個新的React 組件,咱們能夠直接在 Suspense 標籤內使用,這樣組件就會在匹配的時候才加載。json

lazy 接受一個函數做爲參數,函數內部使用 import() 方法異步加載組件,加載的結果返回。segmentfault

Suspense 組件的 fallback 屬性是必填屬性,它接受一個組件,在內部的異步組件還未加載完成時顯示,因此咱們一般傳遞一個 Loading 組件給它,若是沒有傳遞的話,就會報錯。react-router

因此在使用 React Router 4 的時候,咱們能夠這樣寫:dom

import React, { lazy, Suspense } from 'react';
import { HashRouter, Route, Switch } from 'react-router-dom';

const Index = lazy(() => import('components/Index'));
const List = lazy(() => import('components/List'));

class App extends React.Component {
  render() {
    return <div>
      <HashRouter>
        <Suspense fallback={Loading}>
          <Switch>
            <Route path="/index" exact component={Index}/>
            <Route path="/list" exact component={List}/>
          </Switch>
        </Suspense>
      </HashRouter>
    </div>
  }
}

function Loading() {
  return <div>
    Loading...
  </div>
}

export default App;

在某些 React 版本中,lazy 函數還有 bug,會致使 React Router 的 component 屬性接受 lazy 函數返回結果時報錯:React.lazy makes Route's proptypes fail異步

我也遇到了這種 bug,具體的依賴版本以下:async

"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-router-dom": "^4.3.1"

首次安裝依賴後就再也沒有更新過,因此小版本應該也是上面的小版本,不存在更新。

解決方法能夠把 lazy 的結果放在函數的返回結果中:

import React, { lazy, Suspense } from 'react';
import { HashRouter, Route, Switch } from 'react-router-dom';

const Index = lazy(() => import('components/Index'));
const List = lazy(() => import('components/List'));

class App extends React.Component {
  render() {
    return <div>
      <HashRouter>
        <Suspense fallback={Loading}>
          <Switch>
            <Route path="/index" exact component={props => <Index {...props}/>}/>
            <Route path="/list" exact component={props => <List {...props}/>}/>
          </Switch>
        </Suspense>
      </HashRouter>
    </div>
  }
}

function Loading() {
  return <div>
    Loading...
  </div>
}

export default App;

上面代碼和以前惟一的不一樣就是把 lazy 返回的組件包裹在匿名函數中傳遞給 Route 組件的 component 屬性。

這樣咱們的組件都會在路由匹配的時候纔開始加載,Webpack 也會自動代碼進行 code split,切割成不少小塊,減少了首頁的加載時間以及單獨一個 js 文件的體積。在工做中已經實踐過了,確實好用:

pic

若是沒有使用 React v16.6 以上版本,也能夠本身實現,咱們能夠寫一個專門用於異步加載的函數:

function asyncComponent(importComponent) {
  class AsyncComponent extends React.Component {
    render() {
      return this.state.component;
    }
    state = {
      component: null
    }
    async componentDidMount() {
      const { default: Component } = await importComponent();
      this.setState({
        component: <Component {...this.props}/>
      });
    }
  }
  return AsyncComponent;
}

使用的方法與 React.lazy 相同,傳入一個異步加載的函數便可,上面這個函數須要注意的地方就是 import() 進來的組件被包裹在 default 屬性裏,結構時要用 const { default: Component } = ... 這種形式。

效果以下:

pic

總的來講:

  • 新版 React 使用起來更加簡便~
  • 異步組件按需加載這些操做都是基於打包工具的特性,好比 Webpack 的 import
相關文章
相關標籤/搜索