记得在刚开始写代码的时候,那时候做一个网页,用的是PHP,页面内容使用php配合template直出,以为哪个就是一个web开发的全部,不料,react,vue的出现,迅速打破了web开发的体验,如实csr一时间成了前端开发中舆论套路的焦点,随后各种各样的前端开发框架出现,大多数是csr。
代码语言:javascript复制UI = Render(State)
我们为什么需要SSR
从后端拿到数据,在本地渲染出页面,当然这中间还有 虚拟Dom等等,但是本文都不是本文的重点,本文旨在极简的实现一个 SSR,为什么要这么做,因为csr对于一些场景来讲,相对于ssr是有其不足的。具体的一些对比,我将其放在了下面的表格中:
服务端渲染(SSR) | 客户端渲染(CSR) | |
---|---|---|
加载速度 | 通常更快,因为服务器直接发送渲染好的页面,浏览器可以立即显示。 | 通常较慢,因为需要加载JavaScript文件后才能渲染页面内容。 |
SEO优化 | 更有利于SEO,因为搜索引擎可以抓取渲染好的页面内容。 | 不利于SEO,因为搜索引擎可能无法等待JavaScript渲染内容。 |
首屏时间 | 首屏时间短,用户感知到的加载速度更快。 | 首屏时间长,需要等待JS下载和执行。 |
资源利用 | 对服务器资源要求较高,因为渲染工作在服务器上完成。 | 对客户端资源要求较高,渲染工作在用户设备上完成。 |
可交互性 | 页面到达用户浏览器时已经是渲染好的,但需要客户端脚本激活后才能交互。 | 页面加载后即可交互,因为所有渲染和脚本执行都在客户端完成。 |
缓存策略 | 可以利用服务器端缓存来提高响应速度。 | 主要依赖浏览器缓存。 |
开发复杂性 | 通常更复杂,需要处理服务器和客户端代码的同步问题。 | 相对简单,因为所有逻辑都在客户端处理。 |
用户体验 | 用户可以更快看到页面内容,但完全交互可能需要更多时间。 | 用户可能会看到加载指示器,直到页面完全可用。 |
更新部署 | 更新可能需要重新部署服务器端代码。 | 更新通常只需要替换静态文件。 |
可维护性 | 需要维护服务器和客户端两套代码,可能增加维护成本。 | 只需维护客户端代码,维护相对简单。 |
可以看到,服务端渲染(SSR)有着客户端渲染(CSR)不可比拟的一些优势,如,对SEO更加友好,用户可以更快的看到内容,首屏时间短等等,但是CSR也并非一无是处,他实现简单,对服务器压力也轻等等。
学习本文,你可以学会基于react express极简实现一个SSR,这里也提供一个思路,比如vue koa可以不可以,当然也是可以的,这个处理的流程是一致的。
开始动手实现
基于react express实现服务端渲染,其大致的流程如下图所示:
其中,最为复杂的地方就是 reactApp 和 ReactDOMServer 那块的交互,所谓的服务端渲染,就是在服务端把dom都渲染好,直出给到客户端显示,而所谓的csr,就是这个dom的构建过程在客户端本地。
具体的实现步骤
假设我们已经有了一个react的应用,app.js的代码如下:
代码语言:javascript复制import React from 'react';
const App = () => {
return <div>Hello, SSR!</div>;
};
export default App;
那么,这里面省略一个事件处理如 ,onClick等等。那么,要做到这个极简的是在服务端渲染的,如果试下呢?
其实不难,我们在服务端这样玩:
代码语言:javascript复制import React from 'react';
import ReactDOMServer from 'react-dom/server';
import App from './App';
app.get('*', (req, res) => {
const html = ReactDOMServer.renderToString(<App />);
res.send(`
<!doctype html>
<html>
<head>
<title>SSR with React</title>
</head>
<body>
<div id="app">${html}</div>
<script src="/bundle.js"></script>
</body>
</html>
`);
});
但是,我们似乎忽略了一个问题,一个网页是需要有交互的,比如onClick等时间,服务端直出dom似乎会”忘掉”这些,那么怎么办呢?这时候就需要ReactDOM.hydrate了,这个过程称之为唤醒。其大致的流程如下,其目的就是让页面重新变的可交互。
当然, 在 hydration 过程中,React 会对比服务器渲染的 HTML 和 React 组件树。如果发现差异,React 会尝试以最小的代价更新 DOM,以确保客户端的 DOM 与 React 组件树同步。然而,React 假设服务器端和客户端渲染的输出是一致的,如果不一致,可能会导致 hydration 错误。
这就是实现 React SSR 的基本步骤。用代码简化表示如下:
代码语言:javascript复制import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.hydrate(<App />, document.getElementById('app'));
在 React 18 中,ReactDOM.hydrate
已经被 hydrateRoot
替代,后者是与新的并发特性兼容的,在具体实现的时候,需要关注下API的变化。
总结
当然,在我们实际项目中可能会更复杂,绝对不是一个简单的hello world,比如涉及到:
- 路由处理
- 数据预取
- 状态管理
因此,我们的项目如果要考虑选型使用SSR,建议采用成熟的框架,如react可以使用 next.js ,vue可以采用 nuxt.js ,这些都是非常成熟的,且经过比较多大型项目验证过的框架,可以放心使用,而且在腾讯云上也可以非常方便的体验,搜索 腾讯云 快速部署 Nextjs 框架 ,就可以体验。
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!