sp-boilerplate
Version:
Superproject Demo.
149 lines (126 loc) • 5.13 kB
Markdown
## 模板加载数据
在很多项目开发案例中,我们需要利用同一个模板根据URL请求去获取对应的数据最后渲染出对应的结果,如 `/post/123` `/post/65535` 都会用到同样的 `Post.jsx` 组件来渲染页面。*Super-Project* 支持这一方式的同构。
#### 开发步骤
1. 引入 react-redux 的 `connect`,并对 React 组件进行 `connect()`,无论是否会用到 Redux 相关功能。
2. 在 React 组件中加入静态方法 `static onServerRenderStoreExtend(store)`
* 在该方法中派发(dispatch)一个获取数据方法的 Promise,Promise 中写入 redux dispatch。
* 该方法仅针对服务器环境,服务器渲染时会等待 Promise 执行完成后再渲染模板。
3. React 组件中用标准的逻辑编写客户端环境中执行的获取数据代码。
4. 建议利用 `sp-ui-pagecontainer` 的 `PageContainer` 来管理“载入中”“出错”等状态。
代码示例
```js
// API
import axios from 'axios'
import * as actions from '/redux/actions.js'
const getPostData = (postId) => {
return axios.get('/user/post/?pid=' + encodeURIComponent(postId))
.then(data => {
if (data.data) return data.data
return data
})
.catch(e => {
console.log(e)
})
}
export const fetch = (postId) => {
// 如果传入 redux state,从 state 中得到 ID
if (typeof postId === 'object') {
if (postId.routing && postId.routing.locationBeforeTransitions) {
const segs = postId.routing.locationBeforeTransitions.pathname.split('/')
let indexPrev = -1
segs.some((value, index) => {
if (value === 'posts') indexPrev = index
return indexPrev !== -1
})
postId = decodeURIComponent(segs[indexPrev + 1])
}
}
return (dispatch) => getPostData(postId)
.then(data => {
return dispatch(actions.fetch(
postId,
data
))
}).catch(function (error) {
// console.log(postId, error)
dispatch(actions.fetch(
postId,
{
error: '' + error
}
))
})
}
```
```jsx
// React 组件
import React from 'react'
import { connect } from 'react-redux'
import PageContainer from 'sp-ui-pagecontainer'
import * as actions from '/redux/actions.js'
import * as postInfoApi from '/redux/post-info/apis.js'
import { ImportStyle } from 'sp-css-import'
import style from './styles.less'
export default class extends React.Component {
// only for server render
static onServerRenderStoreExtend(store) {
const state = store.getState()
const dispatch = store.dispatch
const preprocessTasks = []
preprocessTasks.push(
dispatch(postInfoApi.fetch(state))
)
return preprocessTasks
}
renderPostInfo() {
if (!this.props.postInfo && __CLIENT__) {
this.props.dispatch(postInfoApi.fetch(this.props.postId))
return ''
} else {
if (this.props.postInfo.code === 0) {
return (
<div class="body-success">
{this.props.postInfo.data}
</div>
)
} else {
return (
<div className="body-error">
Error {this.props.postInfo.code}
</div>
)
}
}
}
render() {
return (
<PageContainer
className={this.props.className}
isLoading={this.props.isLoading}
isError={this.props.isError}
>
{this.renderPostInfo()}
</PageContainer>
)
}
}
```
#### 行为原理
在服务器渲染时,如果组件存在 `onServerRenderStoreExtend(store)` 静态方法,会等待该方法执行结束后再执行其他渲染流程。通常来说,`onServerRenderStoreExtend(store)` 中执行的代码就是一个 Redux dispatch,将获取到的内容输出到 Redux store 中。
在客户端渲染时,`onServerRenderStoreExtend(store)` 静态方法会被无视,所以需要自行再做一套逻辑。