SSR原理

714 阅读4分钟

【这是我参与更文挑战的第13天,活动详情查看: 更文挑战

react ssr 原理很容易理解,但是仅仅理解这点还不够足以搭建应用骨架,其中最有难度的内容应该是同构。

虚拟dom

平时我们都习惯使用jsx来编写react 的组件。但jsx只是一个抽象的语法糖,看上去是写组件,其实我们写的是对象,只是这样写更方便,更符合我们前端开发者的编写习惯,看上去就像写html,多爽。 虚拟 DOM 除了在渲染时用于提高渲染性能,以最小的代价来更新视图的作用外,另一个作用就是为组件的跨平台渲染提供可能。 虚拟DOM本身 就是一个内存中的对象,通过对象的属性来描述要渲染的具体是什么元素以及内容。

如下:

<nav className="nav">
 	<h1 className="nav-item">首页</h1> 
  	<h1 className="nav-item">文章</h1>
  	<h1 className="nav-item">工具</h1>
  	<h1 className="nav-item">关于</h1>
 <nav>

上面的结构可以转换为下面的对象表示(虚拟 dom)

const tree = {
    tag: 'nav', // 节点标签名
    props: {       // DOM的属性,用一个对象存储键值对
     	 class: 'nav'
    },
    children: [    // 该节点的子节点
        {tag: 'h1', props: {class: 'nav-item'}, children: ['首页']},
        {tag: 'h1', props: {class: 'nav-item'}, children: ['文章']},
        {tag: 'h1', props: {class: 'nav-item'}, children: ['工具']},
        {tag: 'h1', props: {class: 'nav-item'}, children: ['关于']},
    ]
}

既然有了这样的对象,我们就可以轻松的把这个对象转换我们想要的表现形式,比如 html格式,而这个html就是我们要直出的内容。不过这个转换的过程不需要我们来完成,react已经帮我们完成,其本身就已提供了内置方法来支持服务端渲染。

同构

所谓同构,就是指前后端公用一套代码,比如我们的组件可以在服务端渲染也可以在客户端渲染,但都是同一个组件。

当然打造同构应用还有另外一个得天独厚的条件,双端使用同一种语言 - javascript。 SSR 部分我们使用node就能完成,所以我们才可以编写同一套代码供双端执行。 另外还有一个重要的特性也是同构的重要体现,浏览器接管页面后的进一步渲染(交互、事件)过程中,会判断已有的DOM结构和浏览器渲染出的结构是否相同,若相同,则不重复渲染,只需要绑定事件即可

双端对比机制

为了实现服务端渲染,打造同构应用,React内部实现了相关的API,可以让我们方便的将一个组件转换为html字符串。

要使用的api:

// ReactDOMServer 类可以帮我们在服务端渲染组件 - 得到组件的 html 字符串。
import ReactDOMServer from 'react-dom/server';

该模块下有个方法:

  • renderToString() 把一个react组件渲染为原始的HTML
ReactDOMServer.renderToString(element)

用这个方法在服务端生成HTML字符串,然后将该字符串返回给浏览器端,完成页面内容的初始化,同时让搜索引擎可以抓取你的页面来达到优化SEO的目的。

  • renderToStaticMarkup(); 该方法是为了将组件渲染为html字符串,不会带有data-react-checksum属性。
ReactDOMServer.renderToString(element)

和上面方法的能力不同,当然使用场景也不同,如果只是单纯服务端渲染的话可以用该方法,性能肯定要比上面的方法高,因为不需要计算嘛,还能减少直出的内容体积。

性能

在浏览器端渲染时,该方法会最大限度的保留服务端使用renderToString()渲染的内容,同时添加事件绑定等交互。

  • renderToNodeStream 和 renderToStaticNodeStream

另外 react 16 在性能上还做了改进,提供了可以将组件转换为字节流的renderToNodeStream方法。其实使用renderToNodeStream或者renderToString对最终的渲染结果没有影响。不过renderToNodeStream的性能要好的多,可以有效缩短TTFB时间。因为组件渲染为字符串,是一次性处理完后才开始向浏览器端返回结果。而采用流的话,可以边读边输出,可以要让页面更快的展现,缩短首屏展现时间。

同构应用时序图