zzbozheng

2017-10-10 12:04

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条评论

    您需要 注册 一个IMWeb账号或者 才能进行评论。