美文网首页
React-服务器端渲染

React-服务器端渲染

作者: 王_建峰 | 来源:发表于2017-12-13 06:46 被阅读99次
早起

为什么服务器端渲染?

服务器端渲染有几个优点:

  1. 如果http被预先渲染,浏览器的性能就会增加,因为浏览器把所有一起返回,所以浏览器的工作量减少了。另外,HTML页面由CDN缓存,不需要在每一次调用时被重新创建。
  2. 搜索引擎只能编译HTML代码,不能编译JS。目前唯一正确的方式是在服务器端渲染所有搜索引擎相关的信息,不会影响seo。

渲染静态标记

rendrtToStaticMarkup方法接收了一个react组件并将html作为字符串返回。这个可以被一个类似于Handlebars一样的简单引擎模板渲染:

<div>{{{ markup }}}</div>

Reactive组件

组件能被服务器渲染是很好的,但是在大部分情况下是不够的。像事件绑定、属性和状态改变这种完全的交互丢失了。

这些组件只有在React意识到客户端也有组件之后才能被集成。

renderToString方法也把HTML作为string返回,但是与静态变量相比,这个方法还支持客户端交互增量计数器的经典例子:

var Counter = React.createClass({
    getInitialState: function() {
        return {
            count: this.props.initialCount
        };
    },
 
    _increment: function() {
        this.setState({ count: this.state.count + 1 });
    },
 
    render: function() {
        return <span onClick={this._increment}>
            {this.state.count}
        </span>;
    }
});"

这个组件利用如下的例子被服务器渲染:

var React = require('react');
var Counter = React.createFactory(require("./counter"));
var http = require('http');
 
var counterHtml = React.renderToString(
    Counter({ initialCount: 3 })
);
 
http.createServer(function(req, res) {
  if (req.url == '/') {
    res.send('<div id="container">' + counterHtml + '</div>');
  }
}).listen(3000);

如果访问 http://localhost:3000/,浏览器会展示初始数字3.

单击一个span元素,假设会触发一个click事件,什么都不会发生。这里有个很简单的解释:React不知道客户端的这个组件,因此不能够绑定处理事件或者执行再次渲染。

为了响应单击事件,这个组件必须被浏览器再创造。

var Counter = React.createFactory(require("./counter"));

React.render(Counter({ initialCount: 3 }), document.getElementById("container")

例如,我们假设React.js和组件都已经被浏览器加载。

这里例子包含了一点魔法:如果被props属性加载的组件跟服务器端的相同,就不再次渲染。React识别到DOM还没有被改变,但是将来可能改变,为了绑定事件React执行所有必要的步骤。

如果组件不被重新渲染,会对性会能有一定的提升。

Synchronizing props(同步props 属性)

属性背后的原理很简单:客户端需要将props传递给服务器

// server.js
// ...
var props = { initialCount: 3 };
var counterHtml = React.renderToString(
    Counter(props)
);

  res.send(
      '<div id="container">' + counterHtml + '</div>' +
      '<script>' +
        'var Counter = React.createFactory(require("./counter"));' +
        'React.render(Counter(' + safeStringify(props) + '), document.getElementById("container"))' +
      '</script>'
  );"

Note:safeStringify方法可以把JSON安全的嵌入JavaScript标签中。
在第五行这个props{ initialCount: 3 }被传递到服务端组件。在第十二行被传递到客户端。

props也可以被放进一个分开的script标签中

<script id="props" type="application/json">
    {{{ theProps }}}
</script>
<script>
    var props = JSON.parse(document.getElementById("props").innerHTML);
    // ...
</script>

因为第二个script标签现在是完全独立的,可以直接放进counter.jsx中

if (typeof window !== 'undefined') {
    var props = JSON.parse(document.getElementById("props").innerHTML);
    React.render(Counter(props), document.getElementById("container"));
}

更进一步,我们可以把props直接放进组件的渲染方法中:

render: function() {
    var json = safeStringify(this.props);
    var propStore = <script type="application/json"
        id="someId"
        dangerouslySetInnerHTML={{__html: json}}>
    </script>;
 
    return <div onClick={this._increment}>
        {propStore}
        {this.state.count}
    </div>;
}

把props放进渲染方法中不是特别好看,但是优点是负责服务端渲染的所有代码都在React组件中。

组件进入浏览器

除了React,浏览器还需要了解React组件。为了不加载每一个组件,像 Browserify 这样的离散工具创建了完全绑定。我们去看看这个例子(非常基本的)

http.createServer(function(req, res) {
  if (req.url == '/') {
    // ...
  } else if (req.url == '/bundle.js') {
    res.setHeader('Content-Type', 'text/javascript')
    browserify()
      .require('./counter.js', {expose: 'counter'})
      .transform({global: true}, literalify.configure({react: 'window.React'}))
      .bundle()
      .pipe(res)
  }

React本质上是怎么同步参数的?

组件可以通过服务器上的一个包含data-react-checksum属性的renderToString被渲染

<div data-reactid=".pxv0hfgr28" data-react-checksum="85249504">
  4
</div>

进去React源代码看看,(ReactServerRendering.js)展示了后台是怎么运行的:

function renderToString(component) {
    ...
    return transaction.perform(function() {
      var componentInstance = instantiateReactComponent(element, null);
      var markup = componentInstance.mountComponent(id, transaction, emptyObject);
      return ReactMarkupChecksum.addChecksumToMarkup(markup);
    }, null);

addChecksumToMarkup方法创建了一个HTML标记的Adler-32 Checksum组件,并把它附在在服务器端被渲染的组件上。

如果这个组件在随后在客户端被渲染,canReuseMarkup(ReactMarkupChecksum.js)这个方法可以为re-rendering做功能测试

canReuseMarkup: function(markup, element) {
    var existingChecksum = element.getAttribute(
        ReactMarkupChecksum.CHECKSUM_ATTR_NAME
    );
    existingChecksum = existingChecksum && parseInt(existingChecksum, 10);
    var markupChecksum = adler32(markup);
    return markupChecksum === existingChecksum;
}

结论:

这个例子仅仅展示了是怎么工作的,没有必要必须走这一步
有很多优雅的方法同步客户端和服务器端,像fluxible-app(dehydration/rehydration)。

相关文章

  • React-服务器端渲染

    为什么服务器端渲染? 服务器端渲染有几个优点: 如果http被预先渲染,浏览器的性能就会增加,因为浏览器把所有一起...

  • 前后渲染

    [服务器端渲染VS浏览器端渲染][1][2] 阿力瓦.服务器端渲染VS浏览器端渲染.2018.cnblogs ↩ ...

  • React-元素渲染

    元素是构成 React 应用的最小砖块。元素描述了你在屏幕上想看到的内容。与浏览器的 DOM 元素不同,React...

  • 服务端渲染

    服务器端渲染(SSR) 学前准备 ES6 Node Vue 什么是服务器端渲染 前端渲染:html页面作为静态文件...

  • React 实作笔记

    服务器端渲染(与客户端渲染共存) npm i express-react-view服务器端先渲染整个页面,页面组件...

  • React服务器端渲染入门

    React服务器端渲染入门 理解当服务器端接收到请求时, 在服务器端基于React动态渲染页面, 并返回给浏览器显...

  • Next.js 中使用Non-SSR组件

    什么是服务器端渲染? 服务器端渲染是一种在服务器上渲染应用程序以响应每个请求,然后将完整的 HTML 和 Java...

  • 关于 SAP 电商云 Spartacus UI Transfer

    Angular 的 TransferState 类使服务器端渲染 (SSR) 和预渲染 (Prerendered-...

  • react生命周期

    componentWillMount( ) 在渲染前调用,在服务器端也客户端 render( ) 渲染 compo...

  • 前端架构之路(9) - 服务器端渲染(SSR)与 node 中间

    服务器端渲染(SSR)与 node 中间层 1. 什么时候需要“服务器端渲染或 node 中间层” 在前后端分离之...

网友评论

      本文标题:React-服务器端渲染

      本文链接:https://www.haomeiwen.com/subject/txnoixtx.html