# 正文
上一节我们实现了应用骨架的路由同构,这一节我们来实现非常重要的一个环节 - 数据同构。
# 什么是数据同构
整体来说,组件的一些数据需要从接口异步获取后进行渲染,数据同构就是服务端和客户端能够使用同一个数据请求处理方法(一套代码),同一份数据进行组件的渲染。
我们前面实现的组件直出只是将组件转换为了 html字符串,但是并没有具体的数据,顶多就是个静态页。
比如现在有这么一个需求,要从接口获取数据并且渲染到页面上。
以往在单页应用中,我们一般都将数据的数据的请求处理放在compoentDidMount生命周期内,得到数据后更改状态,随之渲染。
异步获取数据
componentDidMount(){
...
fetchData().then(res=>{
this.setState({
list:res.list
});
})
}
在 render 方法内组织数据
render() {
....
let {list} = this.state;
.....
return <>
{list&&list.map(item=>{
return <div>{item.title}</div>
})}
</>
}
上面的代码我们都非常熟悉,以上代码也能在 ssr 模式中执行,但是无法得到我们期望的效果,数据只能在客户端得到,达不到数据直出的效果,查看网页源代码也没有我们想要的数据。

开始的时候我们介绍过一些原理,componentDidMount生命周期只会在浏览器端执行,所以如果想让数据也能在服务端渲染就需要做一些特殊的处理。
接下来我们来实现 react ssr 本应用骨架内的数据同构。
# 数据预取
在客户端,我们在componentDidMount生命周期内执行数据请求方法从接口拿到数据。
在服务端渲染组件的时候要想在直出的组件内容也包含数据,那就需要提前得到数据,然后将数据作为属性传递给组件,在constructor内对组件 state 进行初始化。
当组件有了数据,服务端渲染直出的时候自然就会有数据。
以上这个在服务端渲染前得到数据的过程就是数据预取。
思考两个问题:
问题1:客户端和服务端组件渲染执行的声明周期不同,双端如何使用一套代码,代码如何组织呢?
问题2:真实开发中,浏览器的 fetch api 无法在node 端使用,如何统一呢?
以上两个问题都可以通过同构来解决。
先说问题2,因为比较简单,现在已经有很多同构的库来解决。
比如:isomorphic-fetch,axios,这里我推荐使用axios,对开发者非常友好,可以无差别使用。
那现在回到问题1,解决这个问题前需要回顾下以往的知识。
js里无论是函数还是类,到底都是函数,同时都是特殊的对象。
所以我们可以为这些函数添加属性,这个属性也可以被称作为类的静态方法。
静态方法有什么特点?
不需要实例化就可以访问,像下面这样。
class Foo {
run(){
.....
console.log('hello');
}
}
Foo.method=function(){
console.log('hello method');
}
这有什么作用呢?
上面的代码可以在浏览器端执行,当然也可在 node 端执行。
其实以上思路就是解决问题2的办法,可以把Foo想象成我们的react 组件。
我们可以在 node 端找到这个路由对应的组件,然后调用这个组件的静态方法来实现数据的预取。
梳理下完整的思路
- 约定并为组件添加数据预取的静态方法
- 在服务端查找到当前路由对应的组件
- 调用组件的数据预取方法得到数据
- 将数据作为属性传入组件
- 组件内render做相应的处理
- 服务端直出组件
- 浏览器接管页面,完成渲染
# 手膜手实现数据同构
# 约定数据预取方法
首先我们模拟一个异步获取数据的方法,返回一个列表数据。
我这里准备了一份从掘金采集的信息,作为假数据。
// ./src/client/pages/list/data.js
const data = [{
"title": "深入浅出TypeScript:从基础知识到类型编程&qu