服务端渲染

接着上一篇服务端渲染继续写,上一篇是单纯的把一个组件在服务器端渲染为了字符串,但是我们在做单页面的时候是要处理路由的,不同的路由有不同的页面效果,如果要在服务端做渲染就得同时在服务端处理路由.

match, RouterContext

match          // 分析路由模块
RouterContext  //处理分析的结果,根据请求的路由地址将对应的组件获取内容

Basis

server.jsx

import express from 'express';
const app = express();
import { toReactHtml } from "./tostring";

app.get("/:path?*", (req, res) => {
	toReactHtml(req.url)
		.then(({ error ,redirect , html = "" }) => {
			if(error){
				res.status(500).send(error.messsage);	
			}else if(redirect){
				res.redirect(302, redirect.pathname + redirect.search);
			}else{
				res.send(html);	
			}
		});
});

app.listen(8000,"0.0.0.0",function(){
	console.log("open http://127.0.0.1");
});

tostring.jsx

import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router'
import { App } from './router';

function layout(content,data){
	return `
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
    </head>
    <body>
      <div id="app">
          ${content}
      </div>
      <script>
        window.__INITIAL_STATE__ = '${JSON.stringify(data)}';
      </script>
    </body>
    </html>
  `;
}

function toReactHtml(url){
	return new Promise((rsolve)=>{
		match({ routes : App , location: url }, function(error, redirect, props){
			let result = {
				error : null,
				redirect : null
			};
			// 路由解析错误
		    if (error) {
		    		Object.assign(result,{
		    			error : error
		    		});
		    	//重定向
		    } else if (redirect) {
		    		Object.assign(result,{
		    			"redirect" : redirect
		    		});
		    } else if (props) {
				try{
					//渲染组件
					let content = renderToString(
				      	<RouterContext {...props} />
				    );
					Object.assign(result,{
			    			"html" : layout(content, {})
			    		});
				}catch(e){
					console.log(e);
				}
			} else {
		      	Object.assign(result,{
		    			"html" : "Not found"
		    		});
		    }
			rsolve(result);
		});
	});
}

export { toReactHtml };

router.jsx

import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { Layout, Auto, Page , Content } from "./component";

const App = (
	<Router history={ browserHistory }>
		<Route path="/" component = { Layout }>
			<IndexRoute component = { Auto }/>
			<Route path="/page" component = { Page }>
				<Route path=":id" component = { Content }/>
			</Route>
		</Route>
	</Router>
);

export { App };

component.jsx

import { Component, cloneElement } from "react";

class Layout extends Component{
	// 在元素上写 class 需要转换一下,class 是关键字
	//  class 应改为 className
	render(){
		//把传入过来的子组件克隆一份,这个时候就会创建出一个新的组件
		const content = this.props.children ? cloneElement(this.props.children) : Auto;
		let html = (<div className="wrap">
			<div className="content"> { content } </div>
		</div>);
		return html;
	}
}

class Auto extends Component{
  render(){
    return <h1>默认展示的内容</h1>;
  }
}

class Page extends Component{
	render(){
		const content = this.props.children ? cloneElement(this.props.children) : <span>auto</span>;
		return <div>hello Page { content }</div>;
	}
}

class Content extends Component{
	render(){
		return <h1>hello Page Content</h1>;
	}
}
export { Layout, Auto, Page , Content };