目前我们的项目,是单页面程序。第一次加载的时候,是把所有的组件都加载了的。当项目很大的时候,首屏时间可能会变得很长。
我们可以使用异步组件来解决这个问题。我们可以用封装起来的第三方模块来实现,这样会没那么复杂。
react-loadable
我们可以在 github 找到它 https://github.com/jamiebuilds/react-loadable
先下载到项目吧
yarn add react-loadable
下面我们来实现,详情页面的代码,是当我们进入详情页面才会被加载。
我们在 src/pages/detail 下创建一个文件 loadable.js 如下。
loader 是指,我们要异步加载哪个组件。loading 是指在异步等待的过程中要显示的组件内容,这里用的是JSX 语法,因此要引入React。最后,我们把这个组件当一个无状态组件返回。它就能实现异步加载的功能。
import React from 'react';
import Loadable from 'react-loadable';
const LoadableComponent = Loadable({
loader: () => import('./'),
loading () {
return <div>正在加载</div>
},
});
export default () => <LoadableComponent />
接着,我们还要来改一下 src/App.js
将以前的Detail 组件变为这个异步组件。如下。
import React, { Component } from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import { GlobalStyle } from './style.js';
import Header from './common/header';
import store from './store/index';
import Home from './pages/home';
import Detail from './pages/detail/loadable';
import Login from './pages/login';
import WriteArticle from './pages/write';
class App extends Component {
render() {
return (
<div>
<GlobalStyle />
<Provider store={store}>
<div>
<BrowserRouter>
<Header />
<Route path='/' exact component={Home}></Route>
<Route path='/login' exact component={Login}></Route>
<Route path='/write' exact component={WriteArticle}></Route>
<Route path='/detail/:id' exact component={Detail}></Route>
</BrowserRouter>
</div>
</Provider>
</div>
);
}
}
export default App;
这个时候,我们去进入详情也,是会报错的。因为在详情页获得url 中 id 的值的话,这个时候是异步组件才能获得的。而我们在 Detail 中写了获取id 值的代码。
我们可以用过withRouter 方法解决这个问题。最后一行代码withRouter(Detail) 意思是,使Detail 组件有能力获得路由的所有参数和内容。
import React, {PureComponent} from 'react';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { DetailWrapper,
Header,
Content
} from './style';
import { actionCreators } from './store';
class Detail extends PureComponent {
render () {
const { title, content } = this.props;
return (
<DetailWrapper>
<Header>
{ title }
</Header>
<Content dangerouslySetInnerHTML={{__html: content}}>
</Content>
</DetailWrapper>
)
}
componentDidMount () {
this.props.getDetail(this.props.match.params.id);
}
}
const mapState = (state) => {
return {
title: state.get("detail").get("title"),
content: state.get("detail").get("content")
}
}
const mapDispatch = (dispatch) => {
return {
getDetail (id) {
const action = actionCreators.getDetail(id);
dispatch(action);
}
}
}
export default connect(mapState, mapDispatch)(withRouter(Detail));
Done!