引言
我们使用服务端渲染通常是出于这几个原因:
- 更快的首屏加载:用户立即看到内容,无需等待JS加载完成
- 更好的SEO:搜索引擎可以直接索引服务端渲染的内容
- 平滑的交互体验:从静态到交互式的过渡无缝
在实际生产环境中,服务端渲染框架Next.js/nuxt.js提供了完整、稳定、高效的解决方案,能够显著提升开发效率和项目质量。
但是只有看清真相,才能获取真正的自由。
所以,今天我们先来了解下SSR的核心原理-Hydration,相信通过下面简洁明了的内容一定让你看完后惊呼:“欧!!!原来服务端渲染(SSR)是这么回事儿啊!!!”
什么是Hydration(水合)
服务器会根据浏览器URL找到对应路径渲染的 HTML模版,文件下发给客户端。虽然HTML模版具备页面内容,但是并不包含任何动态交互逻辑,比如dom元素点击事件、数据响应式变化。 这里就有“大聪明”要问了,为啥服务器只能生成静态模板,就不能一次性搞定吗?(我曾经就是这样的“大聪明”)
那是因为服务器运行在Node.js环境中:
- 没有DOM(document、window等)
- 没有事件系统(click、hover等)
- 没有CSS样式计算
- 没有用户交互能力
即使这样,模板文件中可以引用一个脚本文件,后面浏览器会下载执行这份 HTML的JS 脚本。通过某种方式将将事件监听、数据状态、生命周期追加到现有的DOM元素上,是的,这里的某种方式就是Hydration(水合)。
看到这可能在想“这是在啰里吧嗦说些什么啊”,接下来我们结合下实际的案例看看吧。
我们先定义下服务器文件
import React from 'react'
import ReactDOM from 'react-dom/server'
import { StaticRouter } from 'react-router-dom'
import App from '../app.jsx'
const express = require('express');
const server = express();
// 关键:静态资源目录,client_bundle.js就放在这里
server.use(express.static("build"));
//在‘/’路由下返回模板资源
server.get('/', (req, res) => {
const AppHtmlString = ReactDOM.renderToString(
<StaticRouter location={req.url}>
<App /> //这是一个APP组件:服务器和客户端都引用它,
//这样确保客户端代码检查现有的DOM结构与服务端渲染的HTML匹配,才能进行Hydration
</StaticRouter>
)
res.send(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
//服务端渲染的静态内容
<div id="root">${AppHtmlString}</div>
//关键:客户端脚本,执行Hydration
<script src="/client/client_bundle.js"></script>
</body>
</html>
`);
});
server.listen(3000, () => {
console.log('Server is running on port 3000');
})
这样你在浏览器看到的就是下面图片内容:
那么这里的脚本文件client_bundle.js是啥呢?
import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import App from '../app.jsx'
//客户端hydrate
ReactDOM.hydrateRoot(document.getElementById('root'), (
<BrowserRouter>
<App /> //注意了:客户端代码检查现有的DOM结构与服务端渲染的HTML匹配,才能进行Hydration
</BrowserRouter>
))
通过前面的步骤我们就实现了SSR的核心功能:客户端接管服务端渲染的静态HTML,使其变为可交互的应用。那咱们这个demo和nextjs相比还差了什么呢?
| 特性 | 当前项目(核心原理) | Next.js(完整实现) |
|---|---|---|
| SSR渲染 | ✅ 手动实现 | ✅ 自动处理 |
| 水合机制 | ✅ 手动实现 | ✅ 自动优化 |
| 路由同步 | ✅ 基础实现 | ✅ 文件系统路由 |
| 数据获取 | ❌ 需要手动 | ✅ 内置API |
| 代码分割 | ❌ 需要手动 | ✅ 自动优化 |
| API路由 | ❌手动创建 | ✅内置支持 |
| 开发体验 | ❌ 基础 | ✅ 优秀 |
ok,SSR的原理Hydration就先讲到这里了,如果有错误纰漏还请指出。咱们下回再聊聊SSR框架nextjs!