哈哈哈,SSR是啥?

28 次浏览
2023年09月14日创建

有空讲一下SSR。

虽然SSR这块内容,在数据交互处理上有很多方案,但这次只讲最简单的。为了更好的讲述这些内容,需要把时光机调回到1990年,哈哈,这时候虽然我还没出生,但通过书本还是能知道这是一个互联网蛮荒时代。

html+css+javascript


这时候正是web刚出生的时候,互联网上通过浏览器能访问的只有html文件,用户通过浏览器访问,出现的页面类似下面,很原始:

程序员通过<ul>标签展示列表,<p>标签显示段落,<h1>标签来显示标题,<img>标签来显示图片等等,总之就是一大堆的标签在html页面上堆叠起来,就成了一个网页。比如下面这样(左边是HTML代码,右边是浏览器打开的样子):

虽然上面的代码已经很展示网页了,但后来人们发现,如果要想将文字显示带颜色用来提醒用户注意,利用标签来实现比较麻烦,比如要将上次有人开车撞到我家狗显示成红色的字体,就需要定义一个red标签,例如<red>上次有人开车撞到我家狗</red>来做,如果是绿色呢?就会陷入无尽的套叠之中,这时候聪明的程序员们想到,将标签和样式分离开来,就可以解决这个问题了。

比如<span>上次有人开车撞到我家狗</span>这段代码,想要将其声明成红色,只需要同时声明style color为红色就好了,例如这段代码<span style="color:red">上次有人开车撞到我家狗</span>就可以了,于此同时,浏览器能理解style就大功告成了,是的,随着浏览器的发展,在1999年,主流的浏览器都能够支持style这种声明样式的方式。

后来程序员发现,在标签中写style=“color:red”这种方式很不好维护,例如:如果网页所有文字都要红色,我需要每个标签都这样新增style:

为了解决这个问题,于是程序员就设计了一套css规范,声明class来代替style,并将其放到<head>标签中用style包裹起来,当然这也需要浏览器支持:

此时你会发现,如果要换成绿色,只需要改一下color:green就好了:

这样已经比较好维护了,但是程序员又发现,一个网站不可能只有一个页面,会有很多html,相同的样式比如一个圆角、绿色按钮样式,不同页面都想用,于是就想着这些样式还能不能独立在一个文件里。于是就有了css文件引入:

这样不同的文件就都能使用了。程序员说,这样便好用了吧。

诚然,这时候html+css的方式已经能做很多事情了。但是如果想要动态效果的时候就做不了,比如点击按钮后,需要弹出一个对话框,让用户确认,根据用户选择yes or no的确认值来展示不同的网页内容,就实现不了,比如想要动态增加css样式、增加页面标签节点等,都做不了,所以程序员设计了javascript——比如为按钮设计一套事件(被点击),javascript引擎监听用户点击和点击了哪个按钮,做对应的逻辑,例如下面点击按钮点我后,弹出一个对话框,让用户进行操作:

程序员对所有的html标签都设计了一套事件机制以及对html页面增删改查、遍历、对样式各种操作的能力。这样,程序员只要实现好这之间的关系,就能处理非常复杂的逻辑了,至此html+css+javascript一整套就出来了。基于此,因为浏览器很多公司都在开发,比如微软,navigate等,所以标准很混乱,于是就涉及了HTML、CSS、javascript规范,同时很多程序员集结起来开发出了很多前端框架,比如jquery这个javascript框架,可以帮助程序员减少兼容性带来的各种问题、以及用起来更方便的api调用方式。

虽然这时候程序员通过html+css+javascript写了一些复杂的页面,但为了将这些页面能被其他用户访问,需要架设服务器(服务器可以买也可以自己架设),为什么要架设服务器呢,是因为服务器有公网IP(比如去电信买宽带,带公网的IP的这种,外面的人就能访问到这台服务器,因为底层的IP协议层都是通过公网IP走来走去的),公网IP用来在互联网上进行通信。

域名与IP、端口


我们知道,IPv4的地址是类似这样的串:11.47.113.232,比较难记,ipv6的串2001:0DB8:0000:0023:0008:0800:200C:417A,就更难记了,所以为了好记,程序员设计了一套域名规则,通过域名来映射成ip(域名需要在域名注册商那里购买,买了后,在他们提供的平台上,将域名解析到服务器IP),这样当人们通过浏览器访问这个域名的时候,就会访问到这个服务器。例如:将jd.com映射成11.47.113.232,这样访问jd.com时就会走到这个ip,也就是这台服务器。下面都是双向箭头:

但是上面还没说完,有了html+css+javascript写的代码,仍然需要像上图一样,将其部署到我们搭建的服务器中(通常是linux系统),我们将其丢到服务器的/root路径下,并安装一个nginx,并配置nginx将80端口映射到/root路径下。为什么是80端口,是因为用户通过浏览器访问http://www.jd.com时,默认是访问服务器的80端口。什么是端口——可以简单理解为一台服务器就是一栋房子,每个房间都有门牌号,不同端口对应的就是不同的房间,端口号就是门牌号。互联网上的数据交互,可以简单理解为就是送快递,北京xx小区第30栋58号房间发送快递到上海xx小区8栋60号,在整个送快递过程中,就知道源头和结尾,只要这个快递系统正常,怎么绕都能找到对应的地址,而nginx可以理解为这一栋的收发室,负责收揽和派发所有到这栋快递的管理人员。

但北京60号房间之所以能够接受快递,是依赖于这个门牌号生效的,也就是依赖房子有人住,那这一点可以理解为我们写的代码要能通过nginx(或者其他方式)部署了,即做了映射(例如80映射到了/root,root下放了我们写的html+css+javascript)。下面都是双向箭头:

这时候部署的代码都是静态代码,因为没有请求服务器来刷新页面,每个用户看到的内容都是一样的。此时程序员觉得想要每个用户看到的内容不一样,比如农场这样,不同用户进度不一样,所以展示的数据需要来自服务器,并结合前端页面输出结果,在这样的背景下,程序员们五花八门,发明了各种工具和框架思路解决这个问题,其中一个东西叫做JSP。

JSP时代SSR


前端浏览器需要展现的页面是html,而数据要来自服务器,那是否可以让服务器直接包裹用户的数据,组装成html直接返回前端,然后前端浏览器无脑直接渲染就可以了。是的,想的没错,这就是伟大的jsp时代(1998~2010年之间)。JSP时代就是后端返回整个打包好的html,然后返回给前端:

上面的图片里,既包含了html代码,也包含了java代码(<% %>内的代码就是java代码),而这种文件的后缀不是index.html,而是index.jsp,这种文件部署在web服务器比如tomcat中,会被tomcat解析成servlet,用户请求后这个servlet响应后,会返回对应的html。

想要理解这个点,我打个比方:一个魔法师有一种能力,可以给小孩任何他想要的水果——比如小孩想要苹果,魔法师就会在自己的魔法瓶里按照要求,把原材料放进去魔法瓶,一顿苹果咒语操作后就变成一个苹果,给小孩,但是这个魔法瓶本身并不能直接使用,需要在微波炉上才能工作。这样理解的话,苹果是用户请求参数,魔法瓶是jsp页面(里面有页面需要的html、java代码原材料),而微波炉就是tomcat,而微波炉本身不能直接工作,需要依赖整个电、插座等环境,这就是操作系统。所以:简单理解为:有电、插座的环境(操作系统)中有一个微波炉(tomcat),魔法师(部署的服务)通过jsp(魔法瓶)响应用户请求(制作苹果),并最终将苹果返回给小孩(用户),环境、魔法师、魔法瓶都属于服务器。

OK,上面讲了这种模式,会发现,前端html、css、javascript和后端的java代码混合在一起写,这需要后端程序员既了解前端也要了解后端,否则就不好写代码了,改错就完了。这个时期的开发方式通常是:前端写好html,然后传给后端,后端把java代码填进去,最后打包部署生效。

当然你也会发现,这种方式,合作效率很低,前后端需要一起搞,对程序员要求很高,但这种方式,前端没有请求单独请求后端各种接口。为什么?举个例子:比如现在的用户请求农场首页,前端其实同时发送了很多请求到后端,比如首页接口、弹窗接口、任务接口等,而JSP这种方式,前端只需要请求:首页。这里说一下,JSP的方式其实就是最初的SSR(server side render,服务端渲染,即服务端打包好了所有前端需要的数据)。

ajax的出现,CSR、生命周期,组件化,前后端分离


后来在2005年的时候,ajax出来了,这个东西使得客户端浏览器可以直接手动发送http请求到服务器,并接受服务器响应,前端可以根据服务器返回的数据进行各种操作。而这个浏览器支持后,于是大家纷纷抛弃JSP,拥抱ajax开发方式,随着时间的推移,很多基于ajax开发了不同的前端框架比如angular、vue、react,于是前端就开始大放异彩,变得越来越多元了。

为什么ajax的出现这么重要,因为程序员可以利用ajax设计一个页面的生命周期,通过生命周期,可以做到组件化。比如一个html页面在浏览器渲染前、渲染后、更新时、被移除时都提供一个钩子给程序员自己实现,这个实现可以是干任何事情,而且这些生命周期有一个特点,即是如果存在内存里的状态数据通过服务器后发生了变更,会自动重新渲染该组件本身,不用像以前一样手动调用函数去更新这个组件的显示。以之前的实现方式,直接将html文件放在服务器上,没有ajax,只能通过服务器返回一些简单的html内容。但如果有生命周期了,配合ajax,我可以在页面被浏览器加载的时候,通过ajax请求服务器,获取数据后,再通过javascript渲染界面,这就是典型的,目前主流的CSR(client side render)方案,即客户端渲染。此时你会发现,前端可以通过生命周期,将一个页面拆分成很多小块,每个小块单独请求服务器,这就是组件化思想。比如知乎的首页,有列表页、推荐文章、广告等,这三块内容就可以声明为3个组件,每个组件通过生命周期干自己的事情,互不干扰。

这样你会发现,还有一个好处,就是可以前后端分离,前端可以专注于自己页面的实现——组件的搭配,组织方式,生命周期管理等,而后端只需要提供接口的返回数据,写好接口文档,到时候碰一起调试就好了。现在很多公司基本上都是这样的工作方式,前后端分离,效率最大化(相较于JSP,是不是好了很多)。

但是生命周期的这种方式,会带来一个问题:那就是白屏。

比如京东秒杀首页做成如果前后端分离的方式,前端浏览器加载(京东app内嵌套了浏览器引擎)秒杀H5首页,此时组件在装载的时候,需要请求html、再请求javascript、css下载下来比如花了10ms,然后发现组件需要加载发送发送请求到服务器,服务器响应时间300ms,前端此时因为还没数据,所以整体呈现白屏,等数据返回了,需要重新渲染花了150ms,总的加起来就会很耗时,很影响用户体验。

于是需要做成首屏SSR,通过服务器返回完整的html,html、css、javascript都不用下载了,只需要直接渲染html就好了,此时会发现,怎么从SSR(jsp方案)到了CSR(react)后,又来到SSR(node)了。

SSR


这时候的SSR纯粹是为了解决白屏的问题(其实SSR我们使用都是解决SEO的问题,但对于京东APP来说SEO其实无所谓),而服务器的响应时间直接影响了用户白屏时间,所以时间卡的比较严格。

具体解决的思路就是,首屏数据通过node去做。这时候为什么不用JSP,因为JSP的实现方式太反人类了,而Node对html支持更好,因为node本身就是javascript发展出来的用来做服务端的,关于SSR的实现原理单独开篇。

哈哈哈哈哈,以上。