UNPKG

sp-boilerplate

Version:
149 lines (126 loc) 5.13 kB
## 模板加载数据 在很多项目开发案例中,我们需要利用同一个模板根据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' @connect( (store, ownProps) => { const postId = decodeURIComponent('' + ownProps.params.id) const from = ownProps.router.location.query.f || '' let props = { postId: postId, postInfo: store.postInfo[postId], isLoading: store.postInfo[postId] ? false : true, isError: store.postInfo[postId] && store.postInfo[postId].code !== 0 ? true : false } if (/^profile\-/g.test(from)) props.fromProfile = from.split('profile-')[1] return props } ) @ImportStyle(style) 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)` 静态方法会被无视,所以需要自行再做一套逻辑。