路由
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'),
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页面提供了详细的介绍,有兴趣的读者可以自行了解下。
至此,路由配置与组件分割的内容完毕。
react扬帆启航专栏分享的时我个人学习与实践react过程中的一些历程,希望借此专栏与大家共同探讨react相关的技术,以求进步。