服务端渲染

准备动作

1、安装nodejs与安装express

安装nodejs教程:http://www.cnblogs.com/pigtail/archive/2013/01/08/2850486.html

安装express教程:http://www.expressjs.com.cn/starter/installing.html

2、安装node-jsx(使nodejs支持jsx语法)

$ npm install node-jsx

3、安装ejs模板引擎

$ npm install ejs

在项目中建立一个app.js,输入

  1. var express = require('express');
  2. var app = express();
  3. app.get('/', function (req, res) {
  4. res.send('Hello World!');
  5. });
  6. var server = app.listen(3000, function () {
  7. console.log("请在浏览器访问:http://localhost:3000/");
  8. });

通过如下命令启动此应用:

  1. $ node app.js

打开浏览器,输入localhost:3000就看到效果了。

使用模板引擎,我们在建立一个test目录,里面再建一个views目录,模块文件都放里面。

  1. var express = require("express");
  2. var app = express();
  3. //指定模板引擎
  4. app.set("view engine", 'ejs');
  5. //指定模板位置
  6. app.set('views', __dirname + '/test/views');
  7. //利用模板文件home.ejs渲染为html
  8. app.get("/", function(req, res) {
  9. res.render('home.ejs', {
  10. name: '司徒正美'
  11. });
  12. });
  13. var server = app.listen(3000, function() {
  14. console.log("请在浏览器访问:http://localhost:3000/");
  15. });

然后我们模块home.ejs

  1. <html>
  2. <head>
  3. <title>my ejs template</title>
  4. </head>
  5. <body>
  6. <p>Hi <%= name %></p>
  7. </body>
  8. </html>

如果你嫌弃ejs后缀文件,你的编辑器无法别识(没有语法高亮),可以改进一下,将home.ejs改为home.html

  1. var express = require("express");
  2. var app = express();
  3. //指定模板引擎
  4. var ejs = require('ejs');
  5. app.set("view engine", 'ejs');
  6. //指定模板位置
  7. app.set('views', __dirname + '/test/views');
  8. //使用ejs模板引擎解析html视图文件
  9. app.engine('.html',ejs.__express);
  10. //利用模板文件home.ejs渲染为html
  11. app.get("/", function(req, res) {
  12. res.render('home.html', {//这里指定文件名
  13. name: '司徒正美'
  14. });
  15. });
  16. var server = app.listen(3000, function() {
  17. console.log("请在浏览器访问:http://localhost:3000/");
  18. });

我们先看一下官方react15.3如何实现后端渲染的

  1. npm install react
  2. npm install react-dom

在Test目录下建立一个components目录,里面建一个Test.js,表示这里是一个类

  1. var React=require("react");
  2. class Test extends React.Component{
  3. render(){
  4. return <h1>{this.props.name}</h1>;
  5. }
  6. }
  7. module.exports = Test

然后修改app.js

  1. var express = require("express");
  2. var app = express();
  3. //指定模板引擎
  4. var ejs = require('ejs');
  5. app.set("view engine", 'ejs');
  6. //指定模板位置
  7. app.set('views', __dirname + '/test/views');
  8. //使用ejs模板引擎解析html视图文件
  9. app.engine('.html',ejs.__express);
  10. //................
  11. //安装"node-jsx",安装该模块可以使nodejs兼容jsx语法
  12. require("node-jsx").install()
  13. var React = global.React = require("react");
  14. var ReactDOMServer = require('react-dom/server')
  15. var Test = require('./test/component/Test.js') //引入React组件
  16. //利用模板文件home.ejs渲染为html
  17. app.get("/", function(req, res) {
  18. res.render('home.html', {//这里指定文件名
  19. component: ReactDOMServer.renderToString( React.createElement( Test,{name:"司徒正美"}) )
  20. })
  21. })
  22. //................
  23. var server = app.listen(3000, function() {
  24. console.log("请在浏览器访问:http://localhost:3000/");
  25. });

然后将模板改一下

  1. <html>
  2. <head>
  3. <meta charset="utf-8">
  4. <meta name="viewport" content="width=device-width">
  5. <title>react 后端渲染</title>
  6. </head>
  7. <body>
  8. <div id="container">
  9. <%-component%>
  10. </div>
  11. <!--使用ejs模板解析后的html字符串-->
  12. </body>
  13. </html>

如果想使用anu的后端渲染方案,主要改一下链接就是

  1. var express = require("express");
  2. var app = express();
  3. //指定模板引擎
  4. var ejs = require('ejs');
  5. app.set("view engine", 'ejs');
  6. //指定模板位置
  7. app.set('views', __dirname + '/test/views');
  8. //使用ejs模板引擎解析html视图文件
  9. app.engine('.html',ejs.__express);
  10. //................
  11. //安装"node-jsx",安装该模块可以使nodejs兼容jsx语法
  12. require("node-jsx").install()
  13. var React = global.React = require("./dist/React");
  14. //var ReactDOMServer = require('react-dom/server')
  15. var ReactDOMServer = require('./dist/ReactDOMServer')
  16. var Test = require('./test/components/Test.js') //引入React组件
  17. //利用模板文件home.ejs渲染为html
  18. app.get("/", function(req, res) {
  19. res.render('home.html', {//这里指定文件名
  20. component: ReactDOMServer.renderToString( React.createElement( Test,{name:"司徒正美"}) )
  21. })
  22. })
  23. //................
  24. var server = app.listen(3000, function() {
  25. console.log("请在浏览器访问:http://localhost:3000/");
  26. });

但现在前端是一个静态页面,没有JS ,我们让它能活动起来设置一下静态资态的目录,我把React.js, babel.js什么放到这里上

  1. //app.js
  2. app.use(express.static('dist'));

重写一下Test目录,让它有事件

  1. var React=require("../../dist/React");
  2. class Test extends React.Component{
  3. click(){
  4. console.log('=========')
  5. }
  6. render(){
  7. return <h1>{this.props.name}
  8. <p onClick={this.click.bind(this)}>事件</p>
  9. </h1>;
  10. }
  11. }
  12. module.exports = Test

home.html也改一下

  1. <html>
  2. <head>
  3. <meta charset="utf-8">
  4. <meta name="viewport" content="width=device-width">
  5. <title>react 后端渲染</title>
  6. <script src='React.js'></script>
  7. <script src='babel.js'></script>
  8. <script type='text/babel'>
  9. class Test extends React.Component{
  10. click(){
  11. console.log('=========')
  12. }
  13. render(){
  14. return <h1>{this.props.name}
  15. <p onClick={this.click.bind(this)}>事件</p>
  16. </h1>;
  17. }
  18. }
  19. window.onload = function(){
  20. ReactDOM.render(<Test name='司徒正美' />, document.getElementById('container'))
  21. }
  22. </script>
  23. </head>
  24. <body>
  25. <div id="container">
  26. <%-component%>
  27. </div>
  28. <!--使用ejs模板解析后的html字符串-->
  29. </body>
  30. </html>

这个后端渲染与前端渲染有什么区别呢?后端渲染会为你的根组件生成的标签添加两个属性data-reactrootdata-react-checksum。其中后者是为了兼容官网React,anu只需要前者就行了。在前端的ReactDOM.render方法里面,anu会检测插入位置的所有直接孩子,判定它有没有data-reactroot属性,有则进入对齐模式。对齐模式与传统的创建模式不一样。

创建模式是根据虚拟DOM创建一棵真实DOM树,然后移除原容器的所有孩子,插入其中。

对齐模式是因为后端已经将所有孩子直接创建好,但可能会多出一些文本节点。这时它只根据虚拟DOM 的type与真实DOM 的node.toLowerCase()进行比较就是。速度肯定快上几个数量级。

而在实际项目中,我们可以通过babel将Test(会去掉里面的module.export = Test)及其他代码进行打包,不会直接写在页面上的。这样一来 ,就可以达到前后共享一套代码。