网页的本质就是超级文本标记语言,通过结合使用其他的Web技术(如:脚本语言、公共网关接口、组件等),可以创造出功能强大的网页。因而,超级文本标记语言是万维网(Web)编程的基础,也就是说万维网是建立在超文本基础之上的。超级文本标记语言之所以称为超文本标记语言,是因为文本中包含了所谓“超级链接”点。
本篇文章给大家带来的内容是关于React中SSR原理的详细介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。随着越来越多新型前端框架的推出,SSR 这个概念在前端开发领域的流行度越来越高,也有越来越多的项目采用这种技术方案进行了实现。SSR 产生的背景是什么?适用的场景是什么?实现的原理又是什么?希望大家在这篇文章中能够找到你想要的答案。
说到 SSR,很多人的第一反应是“服务器端渲染”,但我更倾向于称之为“同构”,所以首先我们来对“客户端渲染”,“服务器端渲染”,“同构”这三个概念简单的做一个分析:
客户端渲染:客户端渲染,页面初始加载的 HTML 页面中无网页展示内容,需要加载执行JavaScript 文件中的 React 代码,通过 JavaScript 渲染生成页面,同时,JavaScript 代码会完成页面交互事件的绑定,详细流程可参考下图(图片取材自 fullstackacademy.com):
服务器端渲染:用户请求服务器,服务器上直接生成 HTML 内容并返回给浏览器。服务器端渲染来,页面的内容是由 Server 端生成的。一般来说,服务器端渲染的页面交互能力有限,如果要实现复杂交互,还是要通过引入 JavaScript 文件来辅助实现。服务器端渲染这个概念,适用于任何后端语言。
同构:同构这个概念存在于 Vue,React 这些新型的前端框架中,同构实际上是客户端渲染和服务器端渲染的一个整合。我们把页面的展示内容和交互写在一起,让代码执行两次。在服务器端执行一次,用于实现服务器端渲染,在客户端再执行一次,用于接管页面交互,详细流程可参考下图(图片取材自 fullstackacademy.com):
一般情况下,当我们使用 React 编写代码时,页面都是由客户端执行 JavaScript 逻辑动态挂 DOM 生成的,也就是说这种普通的单页面应用实际上采用的是客户端渲染模式。在大多数情况下,客户端渲染完全能够满足我们的业务需求,那为什么我们还需要 SSR 这种同构技术呢?
使用 SSR 技术的主要因素:
CSR 项目的 TTFP(Time To First Page)时间比较长,参考之前的图例,在 CSR 的页面渲染流程中,首先要加载 HTML 文件,之后要下载页面所需的 JavaScript 文件,然后 JavaScript 文件渲染生成页面。在这个渲染过程中至少涉及到两个 HTTP 请求周期,所以会有一定的耗时,这也是为什么大家在低网速下访问普通的 React 或者 Vue 应用时,初始页面会有出现白屏的原因。
CSR 项目的 SEO 能力极弱,在搜索引擎中基本上不可能有好的排名。因为目前大多数搜索引擎主要识别的内容还是 HTML,对 JavaScript 文件内容的识别都还比较弱。如果一个项目的流量入口来自于搜索引擎,这个时候你使用 CSR 进行开发,就非常不合适了。
SSR 的产生,主要就是为了解决上面所说的两个问题。在 React 中使用 SSR 技术,我们让 React 代码在服务器端先执行一次,使得用户下载的 HTML 已经包含了所有的页面展示内容,这样,页面展示的过程只需要经历一个 HTTP 请求周期,TTFP 时间得到一倍以上的缩减。
同时,由于 HTML 中已经包含了网页的所有内容,所以网页的 SEO 效果也会变的非常好。之后,我们让 React 代码在客户端再次执行,为 HTML 网页中的内容添加数据及事件的绑定,页面就具备了 React 的各种交互能力。
但是,SSR 这种理念的实现,并非易事。我们来看一下在 React 中实现 SSR 技术的架构图:
使用 SSR 这种技术,将使原本简单的 React 项目变得非常复杂,项目的可维护性会降低,代码问题的追溯也会变得困难。
所以,使用 SSR 在解决问题的同时,也会带来非常多的副作用,有的时候,这些副作用的伤害比起 SSR 技术带来的优势要大的多。从个人经验上来说,我一般建议大家,除非你的项目特别依赖搜索引擎流量,或者对首屏时间有特殊的要求,否则不建议使用 SSR。
好,如果你确实遇到了 React 项目中要使用 SSR 的场景并决定使用 SSR,那么接下来我们就结合上面这张 SSR 架构图,开启 SSR 技术点的难点剖析。
在开始之前,我们先来分析下虚拟 DOM 和 SSR 的关系。
SSR 之所以能够实现,本质上是因为虚拟 DOM 的存在
上面我们说过,SSR 的工程中,React 代码会在客户端和服务器端各执行一次。你可能会想,这没什么问题,都是 JavaScript 代码,既可以在浏览器上运行,又可以在 Node 环境下运行。但事实并非如此,如果你的 React 代码里,存在直接操作 DOM 的代码,那么就无法实现 SSR 这种技术了,因为在 Node 环境下,是没有 DOM 这个概念存在的,所以这些代码在 Node 环境下是会报错的。
好在 React 框架中引入了一个概念叫做虚拟 DOM,虚拟 DOM 是真实 DOM 的一个 JavaScript 对象映射,React 在做页面操作时,实际上不是直接操作 DOM,而是操作虚拟 DOM,也就是操作普通的 JavaScript 对象,这就使得 SSR 成为了可能。在服务器,我可以操作 JavaScript 对象,判断环境是服务器环境,我们把虚拟 DOM 映射成字符串输出;在客户端,我也可以操作 JavaScript 对象,判断环境是客户端环境,我就直接将虚拟 DOM 映射成真实 DOM,完成页面挂载。
其他的一些框架,比如 Vue,它能够实现 SSR 也是因为引入了和 React 中一样的虚拟 DOM 技术。
好,接下来我们回过头看流程图,前两步不说了,服务器端渲染肯定要先向 Node 服务器发送请求。重点是第 3 步,大家可以看到,服务器端要根据请求的地址,判断要展示什么样的页面了,这一步叫做服务器端路由。
我们再看第 10 步,当客户端接收到 JavaScript 文件后,要根据当前的路径,在浏览器上再判断当前要展示的组件,重新进行一次客户端渲染,这个时候,还要经历一次客户端路由(前端路由)。
那么,我们下面要说的就是服务器端路由和客户端路由的区别。
SSR 中客户端渲染与服务器端渲染路由代码的差异
实现 React 的 SSR 架构,我们需要让相同的 React 代码在客户端和服务器端各执行一次。大家注意,这里说的相同的 React 代码,指的是我们写的各种组件代码,所以在同构中,只有组件的代码是可以公用的,而路由这样的代码是没有办法公用的,大家思考下这是为什么呢?其实原因很简单,在服务器端需要通过请求路径,找到路由组件,而在客户端需通过浏览器中的网址,找到路由组件,是完全不同的两套机制,所以这部分代码是肯定无法公用。我们来看看在 SSR 中,前后端路由的实现代码:
客户端路由:
const App = () => {
return (
<Provider store={store}>
<BrowserRouter>
<p>
<Route path='/' component={Home}>
</p>
</BrowserRouter>
</Provider>
)
}
ReactDom.render(<App/>, document.querySelector('#root'))