内容简介:Server-Side Rendering :SSR 是一种前端框架能够在后端渲染出HTML的能力。那些能够在客户端和服务端完成渲染的应用就叫做为了理解为什么需要SSR,这里我们需要了解下web应用在过去十年内的发展史。SSR与SPA(Single Page Application)的兴起紧密相连。与传统的服务端渲染的app相比,SPA在速度和用户体验发面存在很大的优势。 但是使用SPA有个问题,通常情况下用户第一次请求会返回一个空html文件和一堆JS和CSS链接,渲染html之前会先把JS和CSS提前
Server-Side Rendering :SSR 是一种前端框架能够在后端渲染出HTML的能力。那些能够在客户端和服务端完成渲染的应用就叫做
为了理解为什么需要SSR,这里我们需要了解下web应用在过去十年内的发展史。SSR与SPA(Single Page Application)的兴起紧密相连。与传统的服务端渲染的app相比,SPA在速度和用户体验发面存在很大的优势。 但是使用SPA有个问题,通常情况下用户第一次请求会返回一个空html文件和一堆JS和CSS链接,渲染html之前会先把JS和CSS提前下载下来。 这就意味着首次渲染的时候,用户必须要等更长的时间。同时对于爬虫来说,解析到的页面也是一个空页面。 因此SSR的主要思想就是首次在server端渲染应用,后续可以充分利用SPA的优势,在客户端完成渲染。 SSR + SPA = Universal App 有的文章中也会把Universal App讲成isomorphoic app,实际上这两个是同一个东西。采用SSR的情况下,首次渲染的时候,用户不需要等JS加载完成后在看到渲染完成的页面,而是在请求返回的时候就已经拿到渲染完成的页面了。
对于使用slow 3G的用户来说,使用SSR会大大改善用户体验。用户将会直接看到网页内容而不是等待20s+才能看到网页内容。
现在情况下,所有发送到server端的请求都会被直接返回成HTML。这样对于做SEO的部门来说也是十分有利的。 对爬虫来说不会区别对待SPA引用和其他静态站点,同样会为服务端渲染的内容生成索引。 简而言之,使用SSR有两点好处:
- 首次渲染速度更快
- 生成的HTML内容可以被索引到。
一步步来理解SSR
下面笔者将通过一个例子,一步步来实现一个完整的SSR案例。首先从React的服务端渲染API开始,后续每一步我们都将加入一些新的内容。 可以follow 这个项目仓库 ,每一步的代码都会有一个tag,读者可以通过 git checkout tags/xxx -b xxx
的方式获取每一步的代码(xxx为对应的tag名)。
Basic Setup
开始介绍SSR之前,我们需要一个server。这里笔者采用express来渲染React应用。
在代码的第10行,我们用express启动了一个静态服务器。同时我们也创建了一个用于处理非静态请求的handle函数。非静态的路由将会返回HTML代码。
在代码的第13~14行,我们用 renderToString
函数把一开始的JSX代码转换成字符串,这段字符串后续将被插入到HTML模板中。
PS:我们并没有直接启动sever.js ,而是通过index.js来启动server.js。在index.js中,我们用babel插件来抹平client和server端的差异,保证client和server都能够使用es module和jsx。
在SSR中client端的代码也需要从 ReactDOM.render
的改成 ReactDOM.hydrate
。这个函数将会接受服务端渲染的react代码并挂载事件处理函数。
想看到完整的例子,可以check react-ssr tag为basic的代码。到这儿为止,我们就完成了一个简单的服务端渲染的react app。
React Router
到目前为止,我们的应用实际上啥事也没干。现在我们来往之前的应用加入一些路由。先来看看如何处理服务端部分:
现在 Layout
组件在client端上将会渲染出路由组件。对应的我们需要在server端模拟出client的路由实现。下面我们列出 server
端代码的修改部分:
在服务端的代码中,我们需要把React Application包装在 StaticRouter
组件中,并提供 location
参数。 PS: context
用于在渲染 React DOM
的过程中追踪可能的重定向请求:比如client需要根据3XX响应重定向。 完整的案例需要checkout tag为 router
的代码。
Redux
在项目已经具备路由能力的情况下,下面我们来集成 redux
。一些场景下,我们需要使用redux来管理client端的状态。但是在服务端渲染的情况,如何根据当前状态来渲染部分DOM是个问题,因此我们有必要在服务端初始化redux。 如果应用在服务端dispatch action的情况下,SSR需要记录下这些操作,并把最终的state和HTML一起返回给client。在client端,会把服务端返回的state设为redux的初始状态。
我们先来看看server端的实现:
这段代码看起来实现的十分丑陋,但是我们确实需要把服务端渲染出来的redux状态和HTML代码一起返回给client。 接着来看看client部分的实现:
这里我们调用了两次 createStore
,一次在server端,一次在client端。但是在client端上需要把server端保存下来的状态设为redux的初始状态。
完整的例子可以看当前项目的 redux
tag。
Fetch Data
最后一步就是加载数据。这是个比较棘手的问题。我们从一个返回JSON数据的接口开始讲起。 在代码仓库中,笔者通过开放API获取了2018第一赛季的Formula的数据。我们希望在Home页面显示所有的Formula数据。
我们可以在所有 React app
挂载完成、所有元素都已经渲染完毕的的情况下调用API接口来完成需求。如果这样的话,可能会存在一些loading画面,对于用户体验并不友好。
考虑到项目中已经整合了 Redux
,我们可以通过 Redux
来保存数据并返回给前端的方式来加载数据。 如何在server端调用API接口、将接口返回数据保存在Redux中并让客户端根据相关数据来渲染HTML呢? 那么需要调用哪些接口呢? 首先我们需要通过一种不同的方式来申明路由。所以我们把路由改成如下所示:
同时我们也需要在组件上声明所有的数据:
PS: fetchData
是一个 Redux thunk action
,dispatch fetchData的时候会返回一个 promise
。 同时在服务端,我们也用了一个 react-router
中的特殊的函数: matchRoute
:
通过这个方法,当服务端根据当前URL渲染页面的时候会得到需要被 mounted
的组件。我们会收集所有组件需要的数据,等待所有接口都已经返回数据,并把获取的数据塞到redux中才会继续执行服务渲染。 切换到tag为 fetch-data
的分支可以看到整个案例。
从这儿开始,我们就会开始从各个维度进行比较,并比较出哪些场景适合使用SSR哪些场景不适合使用SSR。比如说对一个电商app来说,获取所有的产品是重中之重,但是价格以及一些其他的边栏filter相比之下就显得不那么重要。
Helmet
最后我们来看看SEO。当和React打交道的时候,我们经常需要在 <head>
标签中设置不同的值。比如: title
、 meta tags
、 keywords
等等。 记住 <head>
标签中的内容一般不是 React App
的一部分。 react-helmet 就是为了解决修改 <head>
标签中的内容而生的,并对SSR提供了良好的支持。
你可以在组件树中的任何地方加入head标签内的数据。在client端上,react-helmet提供了一种修改 React App
以外部分的能力。 我们也在SSR中加入这种能力:
现在我们已经显示了一个具备基础功能的React服务端渲染的案例。我们从一个返回HTML内容的express应用开始,慢慢加入了路由、状态管理以及获取数据的能力。最后我们还处理 React App
之外的部分。完整的代码在master分支可以看到。
Conclusion
正如本文所示,SSR并不适合一件难事,但是SSR也可以做的很复杂。如果一步一步来实现需要会更容易些。那么项目中是否需要加入SSR呢?具体情况具体分析。如果网站访问量很大,则建议做SSR。但是如果你的应用是类似于 工具 或者dashboard这种应用,则没必要花费较多的精力来实现SSR。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
电商运营之道:策略、方法与实践
吴伟定、姚金刚、周振兴、郑琰 / 机械工业出版社 / 2015-9-1 / 49
电商运营之道:策略、方法与实践是一本电商的运营指南,适合所有的电商从业人员阅读,也适合打算进入或打算在电商行业创业的读者朋友阅读。分别从策略、方法与实践三个方面全景式展示电商运营的内在商业规律与管理逻辑。一起来看看 《电商运营之道:策略、方法与实践》 这本书的介绍吧!