React16中的服务端渲染(译)

本文作者:IMWeb zzbozheng 原文出处:IMWeb社区 未经同意,禁止转载

React 16发布了。 React 16有很多令人兴奋的新东西(尤其是Fiber),而且React 16对服务器端渲染所做了许多改进,让我们深入剖析React16的服务端渲染有什么不一样。

React 15 SSR是如何工作的

首先,我们先回顾一下React 15的服务端渲染,为了实现SSR,你可能会用nodejs框架(Express、Hapi、Koa)来启动一个web服务器,接着调用 renderToString 方法去渲染你的根组件成为字符串,最后你再输出到 response。

// using Express
import { renderToString } from "react-dom/server"
import MyPage from "./MyPage"
app.get("/", (req, res) => {
  res.write("<!DOCTYPE html><html><head><title>My Page</title></head><body>");
  res.write("<div id='content'>");  
  res.write(renderToString(<MyPage/>));
  res.write("</div></body></html>");
  res.end();
});

然后,在客户端bootstrap代码,再次使用render方法来生成HTML:

import { render } from "react-dom"
import MyPage from "./MyPage"
render(<MyPage/>, document.getElementById("content"));

讲道理,客启端渲染会使用服务端渲染好的HTML,而不是去更新DOM。

那么,服务端渲染在React 16会有不同呢?

React 16 向后兼容

React开发团队有强烈的意愿表示会向后兼容,如果你的代码能够在React 15中运行,那么也可以在React 16中运行,并且不会出现任何弃用警告,正如上面的代码,他可以很好地运行在React 15 和 React 16 中。如果你在App中使用React 16并且发现错误,请在这里提issue,这将会帮助核心团队修复React 16的各种错误。

render() 变为 hydrate() 当你将直出代码从React 15升级到React 16时,你有可能会在浏览器看到以下警告:

事实证明React 16现在有两种不同的客户端渲染方法:当您仅在客户端呈现内容时,使用render() 方法,如果你在服务端渲染结果之上再次渲染则使用hydrate()方法。因为React向后兼容,在React 16中,render()方法会继续可用于服务端渲染。但为了不出现警告信息你最好使用hydrate()方法来代替render():

import { hydrate } from "react-dom"
import MyPage from "./MyPage"
hydrate(<MyPage/>, document.getElementById("content"))

React 16可以处理数组、字符串和数值 在React 15中,,一个组件的render方法必须返回单一的React元素。在React 16, 客户端渲染和服务端渲染允许组件的render 方法返回字符串,数值或者是一个元素数组。

所以,你可以在服务端渲染中写类似以下的代码

class MyArrayComponent extends React.Component {
  render() {
    return [
      <div key="1">first element</div>, 
      <div key="2">second element</div>
    ];
  }
}
class MyStringComponent extends React.Component {
  render() {
    return "hey there";
  }
}
class MyNumberComponent extends React.Component {
  render() {
    return 2;
  }
}

您甚至可以将一个字符串,数字或一组组件传递给顶层的renderToString方法:

res.write(renderToString([
      <div key="1">first element</div>, 
      <div key="2">second element</div>
    ]));
// it’s not entirely clear why you would do this, but it works!
res.write(renderToString("hey there"));
res.write(renderToString(2));

这样你就可以不用为React组件添加div和span,从而使减少HTML的体积。

React16 会更快

说到性能,尽管我们对每一个地方都做到了最佳实践,但是生产环境中的React服务器端渲染依然很慢。在React 16中,跨多个不同版本的Node的服务器端呈现出现惊人的速度:

当将React 15与process.env进行比较时,节点4大约有2.4倍的改进,节点6的性能提升了3倍,而新的Node 8.4版本的增加了3.8倍。 如果您与React 15进行比较而不进行编译,则React 16在最新版本的Node中的SSR中有一个完整的数量级增益。

为什么React 16 SSR比React 15快得多? 在React 15中,服务器和客户端渲染路径或多或少是相同的代码。 这意味着维护虚拟DOM所需的数据结构都将在服务器呈现时进行设置,即使在对renderToString的调用返回时,vDOM也被丢弃。 这意味着在服务器渲染路径上有很多浪费的工作。

然而,在React 16中,核心团队从头开始重写了服务器渲染器,并且根本没有进行任何vDOM的工作。 这意味着它可以快得多。

我做的测试只是用一个非常简单的递归React组件生成一个span的巨型树,这是一个非常极端的基准,不一定能够反映出真实应用场景。

React 16 支持 Streaming

React 16现在支持直接渲染到节点流。渲染到流可以减少你的内容的第一个字节(TTFB)的时间,在文档的下一部分生成之前,将文档的开头至结尾发送到浏览器。 当内容从服务器流式传输时,浏览器将开始解析HTML文档。

渲染到流的另一个好处是能够响应背压。 实际上,这意味着如果网络被备份并且不能接受更多的字节,则渲染器会获得信号并暂停渲染,直到堵塞清除。 这意味着您的服务器使用更少的内存,并更加适应I / O条件,这两者都可以帮助您的服务器处于具有挑战性的条件。

要使用React 16的渲染流,您需要分别在对应于renderToStringrenderToStaticMarkup的react-dom / server:renderToNodeStreamrenderToStaticNodeStream上调用两个新方法其一。 这些新方法不是返回一个字符串,而是返回一个可读流,一个用于发送字节流的对象的Node Stream类。

当从renderTo(Static)NodeStream返回可读流时,它处于暂停模式,并且没有发生渲染。 只有当您调用read或更有可能将可读流导入到可写流中时,才能启动渲染。 大多数Node Web框架都有一个从Writable继承的响应对象,所以通常可以将Readable传递给响应。

// using Express
import { renderToNodeStream } from "react-dom/server"
import MyPage from "./MyPage"
app.get("/", (req, res) => {
  res.write("<!DOCTYPE html><html><head><title>My Page</title></head><body>");
  res.write("<div id='content'>"); 
  const stream = renderToNodeStream(<MyPage/>);
  stream.pipe(res, { end: false });
  stream.on('end', () => {
    res.write("</div></body></html>");
    res.end();
  });
});

请注意,当我们pipe到响应对象时,我们必须包含可选参数{end:false},以便在渲染器完成时不要自动结束响应。 一旦流完全写入到响应中,我们就可以完成HTML体,并结束响应。

原文: https://hackernoon.com/whats-new-with-server-side-rendering-in-react-16-9b0d78585d67

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏对角另一面

读Zepto源码之Fx模块

fx 模块为利用 CSS3 的过渡和动画的属性为 Zepto 提供了动画的功能,在 fx 模块中,只做了事件和样式浏览器前缀的补全,没有做太多的兼容。对于不支持...

3250
来自专栏前端知识分享

第140天:前端开发中浏览器兼容性问题总结(一)

我们在开发的时候会明确项目要兼容哪些浏览器的最低版本,我之前的项目要求兼容IE8.0以上的版本,Chrome 48以上,FireFox 44以上。有了这些最基本...

1.2K3
来自专栏Golang语言社区

H5一二事 - 要饭的

先回顾一下WEB技术的几个阶段 那么H5肯定不是多了一些标签就完事了,H5也跟酷炫没什么关系,那是CSS3的事情,它更多的职责是功能,而不是外观,是JavaSc...

3068
来自专栏Nian糕的私人厨房

WeChat 文章列表页面(二)

本次的系列博文的知识点讲解和代码,主要是来自于 七月老师 的书籍《微信小程序开发:入门与实践》,由个人总结并编写,关于更多微信小程序开发中的各项技能,以及常见问...

1344
来自专栏Google Dart

AngularDart Material Design 列表 顶

它构成了选择和菜单组件的基础。 MaterialListComponent类充当提供样式和收集项事件的能力的列表的根节点。

1092
来自专栏BestSDK

年薪30万的前端面试题,你能答对几道?|附答案

HTML面试题 1.XHTML和HTML有什么区别 HTML是一种基本的WEB网页设计语言,XHTML是一个基于XML的置标语言 最主要的不同: XHTML 元...

4146
来自专栏前端架构与工程

【翻译】浏览器渲染Rendering那些事:repaint、reflow/relayout、restyle

原文链接:http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/ 有没有被标题中的5个“...

2196
来自专栏更流畅、简洁的软件开发方式

给自定义控件(Web Control)添加事件的几种方法。前两种方法可以不实现IPostBackEventHandler

    写自定义控件已经好久了,也有几个用得时间比较长的,但是对于“事件”一直是比较模糊,没有很详细的理解。     最近升级分页控件,由于原来使用的是VB...

2187
来自专栏从零开始学自动化测试

python测试开发django-6.模板中include使用

当我们打开一个网站的时候,在打开不同的页面时候,会发现每个页面的顶部、底部内容都差不多,这样就可以把这些公共的部分,单独抽出来。 类似于python里面的函数,...

1233
来自专栏数据小魔方

动态图表9|组合框(名称管理器)

今天要跟大家分享的是动态图表9——组合框(名称管理器)! 其实看过最近8篇推送的小伙伴儿大概都能看出来了,我所讲的动态图表制作技巧是沿着这样的思路来的: 数据有...

3928

扫码关注云+社区