路由
react-router v4 发生了巨大改变,由静态路由开始转向动态路由,从此,就像使用普通组件一样来声明路由。其实,路由从此就是一个普通组件。
路由的按需加载的实质是代码分割,react-router
官网有个代码拆分的示例,是基于bundle-loader实现的.现在官网的代码已经改为基于react-loadable实现。
除此之外,我们还可以通过dynamic import
来实现代码分割,这里也是本文使用的方式。
首先,在项目根目录创建AsyncComponent.tsx
:
// 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
:
// 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;
至此,我们完成了路由的按需加载。
基于react-loadable
实现代码分割
按照上述方法,已经实现了代码分割。然而,react-loadable
为我们提供了更好的解决方案。react-loadable
核心实现原理也是通过dynamic import
来实现组件的异步加载。在此基础上,它提供了更为强大的功能,如根据延迟时间确定是否显示loading组件、预加载、支持服务端渲染(关于与服务端渲染的搭配会在后续的文章中提到)等。
OK,现在将上述的App.tsx
改造如下:
// 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'),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页面提供了详细的介绍,有兴趣的读者可以自行了解下。
至此,路由配置与组件分割的内容完毕。
react扬帆启航专栏分享的时我个人学习与实践react过程中的一些历程,希望借此专栏与大家共同探讨react相关的技术,以求进步。
第一期系列文章主要内容有: react的基本环境搭建、路由配置与组建分割、前后端数据交互、状态管理(mobx)、react应用部署以及同构。
第一期系列文章主要记录的是我搭建react的种子项目react-sail的过程。如果你觉得对你有些帮助的话,给react-sail个start吧,有兴趣的朋友可以一起来完善。