以前寫過一篇 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 文件的體積。在工做中已經實踐過了,確實好用:
若是沒有使用 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 } = ...
這種形式。
效果以下:
總的來講:
import
~