项目地址:react-ssr-demo
服务端渲染一般在活动页或者官网项目有用到,有利于SEO和首屏渲染优化。
项目用到React-Loadable
实现异步加载,在服务端渲染时需要对异步加载的组件进行处理实现同步读取数据。
附上异步组件代码
class List extends Component {
constructor(props) {
super(props)
}
static fetchData = async (props, params = {}) => {
await props.dispatch(getArticleList(params))
}
componentDidMount() {
if (!this.props.result) {
const { params = {} } = this.props.match || {}
this.constructor.fetchData(this.props, params)
}
}
render() {
const { result = [] } = this.props
return (
<div className="article-page">
{
result.map(({ id, createTime, title }) => (
<div key={id}>{title}---{createTime}</div>
))
}
<Button type="primary">测试</Button>
</div>
)
}
}
复制代码
因为服务端是没有componentDidMount
方法的,所以添加静态方法fetchData
获取接口数据,当服务端渲染时,匹配到当前路由组件后则调用fetchData
方法,将数据写入redux
。在单页渲染时则直接从componentDidMount
获取即可。
服务端渲染核心代码
export default async (req, res, next) => {
//fetch data before component render
const matchedComponents = matchRoutes(routes, req.url).map(({ route }) => {
if (!route.component.preload) { // 同步组件
return route.component;
} else { // 异步组件
return route.component.preload().then(res => res.default)
}
})
const loadedComponents = await Promise.all(matchedComponents);
const promises = loadedComponents.map(component => {
return component.fetchData ? (component.fetchData(store, req.params || {})) : Promise.resolve(null)
})
await Promise.all(promises).catch(err => console.log('err:---', err))
//匹配路由
if (getMatch(routes, req.url)) {
const context = await renderHtml(req, store)
res.send(context)
} else {
await next()
}
}
复制代码
异步组件主要是通过React-Loadable
的preload
方法获取组件,然后用Promise.all
统一获取数据填充到html
即可