早期的SSR
一般说前后端不分离,指的是早期的开发模式,即前端代码写完后嵌入到后端的JSP/PHP
中。由后端服务渲染完数据后直接返回一个完整的HTML
页面,里面的数据都已经渲染好了。
例如,如下是一个JSP
文件,它的内容是:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ page import="java.text.*" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Hello</title>
</head>
<body>
<% out.println("你好"); %>
<br>
<%!
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年mm月dd日");
String s = sdf.format(new Date());
%>
今天是:<%=s %>
</body>
</html>
当客户端尝试向服务端请求这个页面的时候,服务端会对这个模版先进行计算处理,返回给客户端的页面可能是这样的:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Hello</title>
</head>
<body>
你好
<br>
今天是:2022年05月04日
</body>
</html>
可以直接从这个HTML
页面中提取它的关键内容。如果这个客户端指的是爬虫之类的脚本,那么这个爬虫可以分析HTML
的结构,然后提取出关键内容。
上面的这种方式称为SSR
,即Server Side Rendering。翻译过来的意思就是服务端渲染,很好理解,我们上面页面就是通过服务端渲染出来的,内容都直接包含在HTML
中。
与SSR
相对的叫做CSR
,即Client Side Rendering。翻译过来的意思就是客户端渲染,现在的开发模式,大多数都是CSR
为什么要有CSR
我们先来看看早期的SSR
有什么问题。
乘坐时光机,回到十多年前,我们看看腾讯的官方网站是什么样子:
上面的页面十分的简单,几乎都是静态内容,即不用编写太多的JavaScript
,仅用HTML+CSS
编写页面,然后扔给后端开发人员就可以。
而现在的腾讯官网是这样的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p1uNFWZX-1652852569729)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/127db9469ea643baa4ef5166b117ebaa~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image)]
上面的网页有着大量的交互效果,它们都需要编写JavaScript
来完成,而且整个前端项目也比以前要复杂的多,文件量和代码量都远远超过了早期。
所以我们需要把开发这些页面的工作单独提出来,交由其他的工程师来完成。也明确出现了一个新的工种 - 前端工程师,随着这些年的发展,前端的概念越来越火热,前端工程师的角色也越来越重要,不再局限于开发页面。
那CSR
和SSR
各有哪些不同和优缺点呢?
在回答此问题之前,我们先来看下CSR
模式下,前端工程师的开发流程是怎样的
CSR的运行模式
CSR
典型的代表是SPA
,即单页应用Single Page Application。如今Vue/React
都是这种类型的框架。
客户端通过访问域名,向前端服务器请求静态资源(HTML/CSS/JS),向后端服务器请求数据
可以看到,CSR
模式下,因为前后端的分离,多了一个数据交互的步骤,前端需要通过Ajax/fetch
向后端发送请求才能得到数据,然后再将数据渲染到页面上。
既然是这样,那么我们在上面图片的步骤3得到的HTML就很有可能没有完全“渲染”出来。
例如,可能在步骤3得到这样的HTML
页面:
<!-- 原始页面 -->
<html>
<head>
<title>Hello</title>
</head>
<body>
<div id="app"></h1>
</body>
<script> const div = document.getElementById('app')
fetch('/api/data').then(res => res.json()).then(data => {
div.innerHTML = data.name
}) </script>
</html>
经过步骤5之后,才可能得到完整的渲染好的页面,例如:
<!-- 执行JS后的页面 -->
<html>
<head>
<title>Hello</title>
</head>
<body>
<div id="app">Hello World</h1>
</body>
<script> const div = document.getElementById('app')
fetch('/api/data').then(res => res.json()).then(data => {
div.innerHTML = data.name //假设data.name是Hello World
}) </script>
</html>
可以看到,CSR
模式下,浏览器请求某个域名后得到的HTML
页面,里面可能没有有效的内容。必须执行JS
代码,才能得到完整的HTMl
。
这样的缺点是明显的,不利于爬虫抓取或者SEO,首屏加载速度也更慢。
当然,CSR
模式并不是一无是处。一个最大的好处就是前后端分离,可以让前端和后端的代码解耦合,更加方便管理。
除此之外,还有很多优点:
- 因为前端的静态资源与后端是分开的,可以对静态资源进行
CDN
缓存,提高页面的加载速度。 - 将渲染的资源消耗由服务端转为客户端的浏览器承担,
减轻服务端的压力,后端可以专注于业务逻辑的处理。
- 局部刷新,这也是
Ajax/fetch
带来的好处,通过异步获取数据,修改HTML
可以实现页面的局部刷新。
现在的SSR
如今,虽然我们几乎抛弃了传统的SSR
模式,但是由于SSR
的首屏加载速度等优点。我们仍然需要它,因为直接返回渲染好的HTML
在某些场景下很有用。
但是我们又不想回到过去那种古老的方式,于是,专属于前端的SSR
诞生了。是的,前端也可以单独做SSR
你可能听过Nuxt.js/Next.js
等框架,它们都专注于基于流行的前端框架(Vue/React
)做SSR
。
原理示意图如下:
可以看到,Front End Server
接管了浏览器的初始渲染工作,所以浏览器可以直接得到渲染好的HTML
。
这种方式,结合了早期的SSR
的优点,又保留了SPA
的优点。
目前还不够?
前端的SSR
还存在一些缺点。
似乎我们都忘记了一点,前端的SSR
需要在服务区上跑NodeJs
,所以,我们需要一个NodeJs
的服务器。
这就意味着如果你想要使用SSR
,你需要自己搭建一个NodeJs
服务器。不能使用一些第三方的CDN
服务托管你的前端资源(维护服务器是一件恼火的事情)。
我们再来想一下:
- 为什么我们需要从后端获取数据?
- 因为数据是动态的
- 那假如我们的数据是不变的呢?(个人博客等)
- 是不是就不需要从后端获取数据了?是不是可以把数据直接写入到
HTML
中?
- 是不是就不需要从后端获取数据了?是不是可以把数据直接写入到
是的,SSG
就这么诞生了,根据已有的SPA
,在本地打包的时候,计算生成HTML
页面,然后可以直接部署。使用方式可以参考某个大佬写的vite-ssg
原理如下:
总结
越来越多的名词出现在前端领域,像SSR, SPA, CSR, SSG
,不管是哪一门技术,都给前端开发者带来了很多帮助。或许日后,我们将会看到前端的更多可能。