React + React-Loadable 实现服务端渲染

1,859 阅读1分钟

项目地址: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-Loadablepreload方法获取组件,然后用Promise.all统一获取数据填充到html即可