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相關的技術,以求進步。