前后端分离多年,为何服务端渲染(SSR)重回风口浪尖?
什么是服务端渲染?
咱们先搞明白个事儿,啥叫服务端渲染?服务端渲染的全称是 Server-Side Rendering,简称SSR。
简单说,就是在服务器上把网页生成好,整个的HTML页面生成出来,生成出的页面已经包含了所有必要的数据和结构信息,然后直接发给浏览器进行展现。
这和现在流行的前后端分离设计有很大的区别,在前后端分离中,浏览器首先加载到的是一个基本的框架或者说是空壳页面,随后前端通过异步请求后端API获取到数据,然后再动态构建HTML,最后再让浏览器把页面逐渐展现出来。
Web的发展历程
要深入理解服务端渲染为何又成为热点,我们需要回顾Web的发展历史。
早期Web开发(1990年代至2000年代初):在Web的早期,除了静态页面外,大多数网页的HTML是通过服务端渲染的。ASP、JSP、PHP等脚本语言盛行,它们通常将业务逻辑和HTML模板结合在一起,以处理请求并生成完整的HTML页面返回给客户端。在这个阶段,前后端技术混合使用,开发者往往需要同时具备前端和后端的技能。
AJAX的崛起(2005年左右):Ajax技术的发展和jQuery库的普及使得网页能够动态加载数据,实现局部内容的更新,无需刷新整个页面。这一变化增强了前端的交互能力。尽管如此,服务器端渲染仍是主流,前后端职责分离尚未明显。
前端框架的诞生(2009年以后):随着AngularJS、Ember.js等前端框架的诞生,前端工程师能够创建更复杂的单页应用(SPA)。这些应用通过API获取数据,并在客户端渲染页面,显著提升了用户体验。
现代前端框架推动的前后端分离(2013年至今):React、Vue.js等现代前端框架凭借组件化和虚拟DOM等特性,极大地提升了开发效率和用户体验。同时,RESTful API的设计原则被广泛采纳,使后端更专注于提供稳定且高效的数据接口,前端则专责用户界面和交互逻辑。这种模式下,前后端的职责变得更加明确,代码重用和项目可维护性都有所提升。
那么,既然前端正在向着更加独立自主的方向发展,为何服务端渲染(SSR)又重新受到关注呢?原因主要在于单页应用(SPA)面临的两大挑战:
- SEO不友好:搜索引擎的爬虫可能无法正确地抓取和解析动态加载的内容,对于内部管理系统这没啥影响,但是网络上还有很多站点需要搜索引擎的流量支持。
- 首屏加载时间长:用户必须等待整个JavaScript应用下载并执行完成后,才能看到页面内容,这可能会损失用户体验,特别是在弱网环境下。这为啥成为了一个问题了呢?或许是因为前端应用越来越复杂,体积越来越大,而网络传输速度和前端渲染的速度跟不上这种发展。
为了克服这些挑战,同时保留SPA的优势,现代前端框架引入了服务端渲染功能。这样,前后端可以在某些情况下协同完成渲染任务。这种模式有效地平衡了首屏加载速度、SEO优化和维持SPA的交互体验。
值得注意的是,虽然从技术上看仍然是服务端渲染,但当前驱动这件事的角色实际已转变为前端工程师。在前后端分离这一架构革新之后,前端开发技术得到了迅猛发展,如今前端已经具备充分的能力来高效处理诸如SEO优化、页面加载性能提升等问题,这些与用户体验息息相关的交互问题正是前端的应有工作。
Vue中使用SSR
为了方便理解前端驱动的服务端渲染,这里我们举两个例子。
简单例子
先让我们看一个使用原生Node.js和Vue 3进行SSR的简单例子。
需要安装几个依赖包:
npm init -y
npm install vue@2.7.16 vue-server-renderer@2.7.16 express@4.18.2 --save
然后我们创建一个 server.js 的文件,内容如下:
const Vue = require('vue');
const server = require('express')();
const renderer = require('vue-server-renderer').createRenderer();
// 创建一个Vue实例
const app = new Vue({
data: {
message: 'Hello Vue SSR!'
},
template: `<div>{{ message }}</div>`
});
server.get('*', (req, res) => {
renderer.renderToString(app, (err, html) => {
if (err) {
res.status(500).end('Internal Server Error');
return;
}
res.end(`
<!DOCTYPE html>
<html lang="en">
<head><title>Hello Vue SSR</title></head>
<body>${html}</body>
</html>
`);
});
});
// 监听8080端口
server.listen(8080, () => {
console.log('Server running at http://localhost:8080/');
});
在这个例子中,我们首先导入了vue和express,以及vue-server-renderer。然后,我们创建了一个简单的Vue实例,并定义了一个基本的模板。在server.get回调中,我们使用renderToString方法将Vue实例渲染为字符串形式的HTML。
使用 node server.js 启动后,我们可以访问http://localhost:8080/,就会看到 "Hello Vue SSR!" 的消息。
Nuxt.js的例子
上边的例子非常基础,只是为了演示如何使用Vue进行服务器端渲染。在生产环境中,我们需要考虑很多其他因素,比如路由、状态管理、API交互、构建配置、安全性等。对于复杂的应用程序,需要使用像Nuxt这样的高级框架,专注于编写业务逻辑,基础的东西让框架搞定。
首先通过npx(随Node.js一起安装的包执行器)创建一个nuxt项目,命令中的 ssr-nuxt-demo 是项目的名称。
npx nuxi@latest init ssr-nuxt-demo
这个命令会在当前目录中创建一个名为ssr-nuxt-demo的文件夹,在这个文件夹下自动创建项目的基本结构。
然后我们在项目中创建一个pages文件夹,编写一个index.vue文件,Nuxt会自动为该文件创建路由。
文件内容如下:
<template>
<div>
<h1>{{ message }}</h1>
<button @click="changeMessage">Change Message</button>
</div>
</template>
<script lang="ts">
export default {
data() {
return {
message: 'Hello Nuxt.js SSR!'
}
},
methods: {
changeMessage() {
this.message = 'Hello from the client side!';
}
}
}
</script>
运行 npm run dev,然后在浏览器中访问 http://localhost:3000:
你将看到初始的消息:Hello Nuxt.js SSR!,这是服务端渲染出来的。点击"Change Message"按钮,消息将更改为"Hello from the client side!",这是浏览器中Vue进行的处理。
在这个例子中我们其实展示了一种同构能力,也就是说前后端公用一套代码,我们的组件既能在前端使用,也能在后端使用。也就是我们上文提到的前端驱动服务端渲染的意思。
当然这还是一个简单的例子,不过已经可以让我们体会到Nuxt的强大能力,有兴趣的同学可以去好好体验下Nuxt的强大能力。
以上就是本文的主要内容。
服务端渲染的轮回,似乎让我们看到了某种宿命,不过历史总是在曲折螺旋中上升的,或许某一天我们真的不再需要服务端渲染,这可能是因为通信能力的大幅提升,又或者是流量的某种异变。
关注萤火架构,加速技术提升!