各位大侠,小弟最近搞react服务端渲染,遇到了几个问题,想向大家求证下。
这是node的代码:
import express from 'express';
import path from 'path';
import React from 'react';
import {
match
} from 'react-router';
import routes from '../client/router';
import configureStore from '../client/redux';
import _render from './render';
import * as API from '../client/api';
var app = express();
app.use('/', express.static(path.join(process.cwd(), 'dist', 'client')));
//开发环境没有弄公共文件,直接引入入口文件main.client.js
function renderFullPage(html, initialState) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<div id="app">
<div>
${html}
</div>
</div>
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};
</script>
<script src="/js/main.client.js"></script>
</body>
</html>
`;
}
//返回页面
const getComponentState = (store, renderProps, res) => {
//如果组件有static getInitState()这个方法就获取相关数据后返回
if (renderProps.components[3].getInitState) {
renderProps.components[3].getInitState(store.dispatch).then(ret => {
//__render的方法是引进来的
var html = _render(store, renderProps);
res.end(renderFullPage(html, store.getState()));
}, err => {
console.log(err)
}).catch(e => {});
} else {
var html = _render(store, renderProps);
res.end(renderFullPage(html, store.getState()));
}
}
//当前页面刷新匹配相应的路由
app.use((req, res) => {
console.log(req.url, 'urlurlurlurlurl')
var store = configureStore();
match({
routes,
location: req.url
}, (err, redirectLocation, renderProps) => {
if (err) {
res.status(500).end(`Internal Server Error ${err}`);
} else if (redirectLocation) {
res.redirect(redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
getComponentState(store, renderProps, res);
} else {
res.status(404).end('Not found');
}
});
});
app.listen(3000);
这是客户端Home
首页的代码:
import React from 'react';
import BFooter from '../components/common/Footer';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { browserHistory } from 'react-router';
import ArticleList from '../components/article/ArticleList';
import ACT from '../redux/actions';
import $$ from '../utils';
import * as API from '../api';
import '../assets/css/home/index.css';
class Home extends React.Component {
static getInitState(dispatch){
return new Promise((resolve, reject)=>{
API.getArticleList().then(res=>{
dispatch(ACT.home.setArticleList(res.data));
resolve(res.data);
}, err=>{ reject(err)}).catch(e=>{console.log(e)})
})
}
componentWillMount() {
console.log('server: '+__isServer__, 'client: '+__isClient__ , '平台')
//如果是通过前端路由切换过来的就执行这个方法,因为服务端在前端切换路由的时候,不会执行static
__isClient__ && this.constructor.getInitState(this.props.dispatch);
}
componentDidMount(){
}
componentWillReceiveProps(){
}
render(){
return (
<div className="home">
<div className="main">
<div className="wrapper">
<div className="left-cont">
<ArticleList onLoadMore={()=>{}} isShowMore={true} isMobile={false} isMore={false} articleList={this.props.articleList} />
</div>
</div>
</div>
<BFooter />
</div>
)
}
}
const mapStateProps = state=>{
return {
state,
articleList:state.home.articleList
}
}
const mapDispatchProps = dispatch=>{
return{
dispatch,
getArticleList:(params)=>dispatch(ACT.home.getArticleList(params))
}
}
export default connect(mapStateProps, mapDispatchProps)(Home);
main.js的代码:
import React from 'react';
import { render } from 'react-dom';
import { Router } from 'react-router';
import createBrowserHistory from 'history/lib/createBrowserHistory';
import { Provider } from 'react-redux';
import routes from './router';
import configureStore from './redux';
const initialState = window.__INITIAL_STATE__;
const store = configureStore(initialState);
render(
<Provider store={store}>
{routes}
</Provider>,
document.getElementById('app')
);
export default store;
路由:
import React from 'react';
import {Router, Route, IndexRoute, browserHistory, hashHistory} from 'react-router';
import './assets/css/common/base.css';
import './assets/libs/Font-Awesome-3.2.1/css/font-awesome.min.css';
if (typeof require.ensure !== 'function') {
require.ensure = function(dependencies, callback) {
callback(require)
}
}
const App = (location, cb) => {
require.ensure([], require => {
cb(null, require('./App').default)
},'App')
}
const Home = (location, cb) => {
require.ensure([], require => {
cb(null, require('./views/Home').default)
},'Home')
}
const Search = (location, cb) => {
require.ensure([], require => {
cb(null, require('./views/Search').default)
},'Search')
}
const Message = (location, cb) => {
require.ensure([], require => {
cb(null, require('./views/Message').default)
},'Message')
}
const About = (location, cb) => {
require.ensure([], require => {
cb(null, require('./views/About').default)
},'About')
}
const router = (
<div>
<Router history={browserHistory}>
<Route path="/" getComponent={App}>
<IndexRoute getComponent={Home}/>
<Route path="/search" getComponent={Search}/>
<Route path="/message" getComponent={Message}/>
<Route path="/About" getComponent={About}/>
</Route>
</Router>
</div>
)
export default router;
这里是我遇到的几个问题:
- 只做首页的服务端数据返回渲染,就可以解决一定程度的SEO问题了吗?其他的路由的不想搞服务端渲染,因为我觉得这样会导致redux相关代码的体积过大,加大工作量,所以除了首页外都使用了客户端的获取数据渲染方式,操作组件的自身的state会比较方便。
- 既然服务端渲染是为了减少首屏时间和seo,那么当刷新首页(单独这个页面做服务端渲染),可不可以延迟用户状态的获取,就是在服务端返回无权限数据渲染的html后,重新通过客户端来验证用户状态(cookie和localstorage这种情况下应该都可以使用了,现在还没做用户验证)。
- 现在面临的问题是:每当首页在服务端返回html后,客户端都会重新渲染一遍(查看源代码
data-react-checksum
等属性,但是审查元素却没有该属性了),我对比了一下store的数据,是一致的,为啥客户端还会渲染一次啊?