从零开始React服务器渲染

栏目: IOS · Android · 发布时间: 5年前

内容简介:一.前言当我们选择使用Node+React的技术栈开发Web时,React提供了一种优雅的方式实现服务器渲染。使用React实现服务器渲染有以下1.利于SEO:React服务器渲染的方案使你的页面在一开始就有一个HTML DOM结构,方便Google等搜索引擎的爬虫能爬到网页的内容。

一.前言

当我们选择使用Node+React的技术栈开发Web时,React提供了一种优雅的方式实现服务器渲染。使用React实现服务器渲染有以下 好处

1.利于SEO:React服务器渲染的方案使你的页面在一开始就有一个HTML DOM结构,方便Google等搜索引擎的爬虫能爬到网页的内容。

2.提高首屏渲染的速度:服务器直接返回一个填满数据的HTML,而不是在请求了HTML后还需要异步请求首屏数据。

3.前后端都可以使用js

二.神奇的renderToString和renderToStaticMarkup

有两个神奇的React API都可以实现React服务器渲染: renderToStringrenderToStaticMarkup 。renderToString和renderToStaticMarkup的主要作用都是 将React Component转化为HTML的字符串 。这两个函数都属于react-dom(react-dom/server)包,都接受一个React Component参数,返回一个String。

也许你会奇怪为什么会有两个用于服务器渲染的函数,其实这两个函数是有 区别 的:

1.renderToString:将React Component转化为HTML字符串,生成的HTML的DOM会带有额外属性:各个DOM会有 data-react-id 属性,第一个DOM会有 data-checksum 属性。

2.renderToStaticMarkup:同样是将React Component转化为HTML字符串,但是生成HTML的DOM不会有额外属性,从而节省HTML字符串的大小。

下面是一个在服务器端使用renderToStaticMarkup渲染静态页面的例子:

npm包安装:

npm -S install express react react-dom

server.js

var express = require('express');
var app = express();

var React = require('react'),
 ReactDOMServer = require('react-dom/server');

var App = React.createFactory(require('./App'));

app.get('/', function(req, res) {
 var html = ReactDOMServer.renderToStaticMarkup(
 React.DOM.body(
 null,
 React.DOM.div({id: 'root',
 dangerouslySetInnerHTML: {
 __html: ReactDOMServer.renderToStaticMarkup(App())
 }
 })
 )
 );

 res.end(html);
});

app.listen(3000, function() {
 console.log('running on port ' + 3000);
});

App.js

var React = require('react'),
    DOM = React.DOM, div = DOM.div, button = DOM.button, ul = DOM.ul, li = DOM.li

module.exports = React.createClass({
  getInitialState: function() {
   return {
     isSayBye: false
   }
  },
  handleClick: function() {
   this.setState({
     isSayBye: !this.state.isSayBye
   })
  },
  render: function() {
    var content = this.state.isSayBye ? 'Bye' : 'Hello World';
    return div(null,
      div(null, content),
      button({onClick: this.handleClick}, 'switch')
    );
  }
})

运行:

node server.js

结果:

从零开始React服务器渲染

三.动态的React组件

上例的页面中,点击“switch”按钮是没有反应的,这是因为这个页面只是一个静态的HTML页面,没有在客户端渲染React组件并初始化React实例。只有在 初始化React实例后 ,才能更新组件的state和props,初始化React的事件系统,执行虚拟DOM的重新渲染机制,让React组件真正“动”起来。

或许你会奇怪,服务器端已经渲染了一次React组件,如果在客户端中再渲染一次React组件,会不会渲染两次React组件。答案是不会的。秘诀在于 data-react-checksum 属性:

上文有说过,如果使用renderToString渲染组件,会在组件的第一个DOM带有 data-react-checksum 属性,这个属性是通过 adler32算法 算出来:如果两个组件有相同的props和DOM结构时,adler32算法算出的checksum值会一样,有点类似于哈希算法。

当客户端渲染React组件时,首先计算出组件的checksum值,然后检索HTML DOM看看是否存在数值相同的 data-react-checksum 属性,如果存在,则组件只会渲染一次,如果不存在,则会抛出一个warning异常。也就是说,当 服务器端和客户端渲染具有相同的props和相同DOM结构的组件时,该React组件只会渲染一次

在服务器端使用 renderToStaticMarkup 渲染的组件不会带有 data-react-checksum 属性,此时客户端会重新渲染组件,覆盖掉服务器端的组件。因此,当页面不是渲染一个静态的页面时,最好还是使用 renderToString 方法。

上述的客户端渲染React组件的流程图如下:

从零开始React服务器渲染

四.一个完整的例子

下面使用React服务器渲染实现一个简单的计数器。为了简单,本例中不使用redux、react-router框架,尽量排除各种没必要的东西。

项目目录如下:

从零开始React服务器渲染

npm包安装:

npm install -S express react react-dom jsx-loader

webpack.config.js :webpack配置文件,作用是在客户端中可以使用代码模块化和jsx形式的组件编写方式:

var path = require('path');

var assetsPath = path.join(__dirname, "public", "assets");
var serverPath = path.join(__dirname, "server");

module.exports = [
 {
 name: "browser",
 entry: './app/entry.js',
 output: {
 path: assetsPath,
 filename: 'entry.generator.js'
 },
 module: {
        loaders: [ 
            { test: /\.js/, loader: "jsx-loader" }
        ]
    }

},
{
name: "server-side rending",
entry: './server/page.js',
output: {
path: serverPath,
filename: "page.generator.js",
// 使用page.generator.js的是nodejs,所以需要将
// webpack模块转化为CMD模块
library: 'page',
libraryTarget: 'commonjs' 
},
module: {
loaders: [
{ test: /\.js$/, loader: 'jsx-loader' }
 ]
 }
 }
]

app/App.js :根组件 (一个简单的计数器组件),在客户端和服务器端都需要引入使用

var React = require('react');

var App = React.createClass({
 getInitialState: function() {
        return {
            count: this.props.initialCount
        };
    },

    _increment: function() {
        this.setState({ count: this.state.count + 1 });
    },

 render: function() {
 return (
 <div>
 <span>the count is: </span>
<span onClick={this._increment}>{this.state.count}</span>
 </div>
 )
 }
})

module.exports = App;

server/index.js :服务器入口文件:

var express = require('express');
var path = require('path');

var page = require("./page.generator.js").page;

var app = express();
var port = 8082;

app.use(express.static(path.join(__dirname, '..', 'public')));

app.get('/', function(req, res) {
 var props = {
 initialCount: 9
 };
 var html = page(props);
 res.end(html);
});

app.listen(port, function() {
 console.log('Listening on port %d', port);
});

server/page.js :暴露一个根组件转化为字符串的方法

var React = require('react');
var ReactDOMServer = require("react-dom/server");

var App = require('../app/App');

var ReactDOM = require('react-dom');


module.exports = function(props) {
 
 var content = ReactDOMServer.renderToString(
 <App initialCount={props.initialCount}></App>
 );

 var propsScript = 'var APP_PROPS = ' + JSON.stringify(props);

 var html = ReactDOMServer.renderToStaticMarkup(
 <html>
 <head>
 </head>
 <body>
 <div id="root" dangerouslySetInnerHTML={
 {__html: content}
 } />
 <script dangerouslySetInnerHTML={
 {__html: propsScript}
 }></script>
 <script src={"assets/entry.generator.js"}></script>
 </body>
 </html>
 );

 return html;
}

为了让服务器端和客户端的props一致 ,将一个服务器生成的首屏props赋给客户端的全局变量 APP_PROPS ,在客户端初始化根组件时使用这个APP_PROPS根组件的props。

app/entry.js :客户端入口文件,用于在客户端渲染根组件,别忘了使用在服务器端写入的 APP_PROPS 初始化根组件的props

var React = require('react'),
 ReactDOM = require('react-dom'),
 App = require('./App');

var APP_PROPS = window.APP_PROPS || {};

ReactDOM.render(
 <App initialCount={APP_PROPS.initialCount}/>,
 document.getElementById('root')
);

源代码放在github上,懒得复制粘贴搭建项目的同学可以猛戳 这里

github上还有其他的服务器渲染的例子,有兴趣的同学可以参考参考:

1. 无webpack无jsx版本

2. 使用webpack版本

参考文章:

1. Rendering React Components on the Server

2. 一看就懂的 React Server Rendering(Isomorphic JavaScript)入門教學

3. Clientside react-script overrides serverside rendered props

4. React直出实现与原理

5. React Server Side Rendering 解决 SPA 应用的 SEO 问题

6. Server-Side Rendering with React + React-Router


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

100个可操作的网络赚钱方法

100个可操作的网络赚钱方法

陶秋丰 / 云南科技 / 2009-12 / 29.80元

《100个可操作的网络赚钱方法》专为有志于网上创业的读者量身打造,作者是“实战型”的网赚高手,在17岁时就通过互联网创业“年人10万”,如今结合自身的亲身实战经验,与大家分享可以实实在在盈利的100个网络赚钱方法和技巧。内容包括:网站创建与推广、竞价广告、联盟赚钱、网站SEO优化、域名投资、广告投放盈利、威客、博客、淘客赚钱等多个方面。 本手册中作者结合自身的网络赚钱经历,通过具体的、可操作......一起来看看 《100个可操作的网络赚钱方法》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具