<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Build Tools on 日日是好日</title><link>https://jjjjjjy.github.io/posts/performance/</link><description>Recent content in Build Tools on 日日是好日</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sun, 20 Jul 2025 16:16:27 +0800</lastBuildDate><atom:link href="https://jjjjjjy.github.io/posts/performance/index.xml" rel="self" type="application/rss+xml"/><item><title>CSR和SSR</title><link>https://jjjjjjy.github.io/posts/performance/ssr-csr/</link><pubDate>Sun, 20 Jul 2025 16:16:27 +0800</pubDate><guid>https://jjjjjjy.github.io/posts/performance/ssr-csr/</guid><description>&lt;h1 id="csrclient-side-rendering-客户端渲染"&gt;CSR（Client-Side Rendering）: 客户端渲染
&lt;/h1&gt;&lt;p&gt;服务器主要负责提供静态的HTML文件（可能包含一些基本的HTML结构和JavaScript脚本），而真正的页面渲染工作则完全由客户端的浏览器来完成。这意味着页面内容是在用户的浏览器上动态生成的。&lt;/p&gt;
&lt;p&gt;优点：&lt;/p&gt;
&lt;p&gt;响应速度快：一旦HTML文件加载完成，浏览器就可以开始渲染页面，而不需要等待服务器返回完整的渲染结果。
动态性强：由于页面渲染在客户端进行，因此可以方便地实现各种动态交互效果。
前端部署简单：只需要一个静态服务即可部署前端代码，降低了部署成本。&lt;/p&gt;
&lt;p&gt;缺点：&lt;/p&gt;
&lt;p&gt;首屏加载时间长：由于需要加载整个JavaScript包，可能导致首屏加载时间较长，特别是对于复杂的单页应用（SPA）。
不利于SEO：搜索引擎爬虫可能无法很好地解析由JavaScript动态生成的页面内容，导致SEO效果较差。
白屏时间：在JavaScript代码加载和执行期间，用户可能会看到空白的页面，即所谓的“白屏时间”。&lt;/p&gt;
&lt;h1 id="ssrserver-side-rendering-服务端渲染"&gt;SSR（Server-Side Rendering）: 服务端渲染
&lt;/h1&gt;&lt;p&gt;&lt;strong&gt;SSR 的流程一定要熟：构建 → 请求 → 服务端渲染 → 注水 → 客户端接管&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSR 是一种在服务器端完成页面渲染的技术。&lt;/li&gt;
&lt;li&gt;在SSR下，服务器端接收到客户端的请求后，会根据 &lt;strong&gt;客户端 请求的数据&lt;/strong&gt; 以及 &lt;strong&gt;模板文件&lt;/strong&gt; 生成完整的HTML页面。然后将这个完整的HTML页面直接发送给客户端。&lt;/li&gt;
&lt;li&gt;这样，用户可以直接看到完成的内容，无需等待JavaScript加载和执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ssr-模板文件"&gt;SSR 模板文件
&lt;/h2&gt;&lt;p&gt;👉 SSR 本质上是生成一段 HTML 字符串， 然后插入到 SSR 模板文件 中。&lt;/p&gt;
&lt;p&gt;｜&lt;code&gt;模板文件&lt;/code&gt;: SSR 模板 = 静态 HTML 容器 (纯HTML字符串) + 插槽占位&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSR 模板文件 不会渲染，而是“接收渲染结果”的容器。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;优点：&lt;/strong&gt; 使用SSR模板文件的好处是无需每次构建整个页面，只需要每次将数据插入到slot中，从而渲染插槽即可。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSR模板文件 &lt;strong&gt;不会被&lt;/strong&gt;服务端构建器&lt;strong&gt;编译&lt;/strong&gt;（build/compile）
&lt;ul&gt;
&lt;li&gt;构建器（如 Vite、Webpack、Rollup）并不会处理这个 index.html 模板（它可能会被拷贝，但不会编译、分析依赖、生成 bundle）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SSR模板文件 &lt;strong&gt;不会被&lt;/strong&gt;客户端解析模板语法而&lt;strong&gt;渲染&lt;/strong&gt;（render）
&lt;ul&gt;
&lt;li&gt;SSR模板文件 不会被 像 Vue/React/San 这样的框架渲染，因为浏览器只会看到它已经被替换好的 HTML，根本不会看到插槽占位&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SSR模版只是 在 node 读取模版时，使用JS 将 插槽占位 替换成 服务端解析客户端获得的数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;与slot的区别:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSR 插槽 = 等待“某个组件渲染结果”的静态 HTML 占位符&lt;/li&gt;
&lt;li&gt;组件插槽 = 等待“其他组件传入内容”的动态子组件插入点&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ssr-反解--hydration注水机制"&gt;SSR 反解 —— Hydration（注水）机制
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;🌟 既然SSR是在服务端渲染一个HTML的过程，那么客户端是如何接管运行的呢？如何执行页面上的JS逻辑呢？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这就涉及到 SSR反解，即挂载JS的过程。 又称 Hydration（注水）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SSR 注水时的 JS 逻辑，是浏览器从 HTML 中加载 script 脚本文件后执行的，而注水的入口逻辑 就是 JS 应用接管 DOM 的起点。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;SSR 的“反解”就是客户端的 Hydration（注水）过程，即将 SSR 渲染好的 HTML，挂上 JS 逻辑，变成真正可交互的应用。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;🔥 注水机制的 &lt;strong&gt;本质&lt;/strong&gt; 是在 &lt;strong&gt;已有DOM上挂载JS逻辑&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;❌ 这里需要区分CSR。 Hydration 是 SSR 独有的步骤，纯 CSR 是 没有 Hydration过程的，因为CSR 不会预先接管HTML，而是自己从 JS 渲染 DOM。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;React 的 hydrateRoot()、Vue 的 app.mount()、san的 myComponent.attach(el, { ssr: true }) 都是注水入口。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;但是 有的时候 我们也会在 CSR 项目里看到下面这种代码：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const app = createApp(App);
app.mount(&amp;#39;#app&amp;#39;);
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;但这其实不是注水，这里 #app 是空的，App 是从 0 渲染出来的。只是 API 长得像 SSR 的注水 mount()，但它做的是“创建 DOM”，不是“复用已有 DOM”。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SSR 的 HTML 是“服务器生成的”，DOM是在服务端生成的，Hydration 是“接管”， 只需要在 DOM上挂载，而无需重新创建DOM；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在CSR中，服务器返回一份 空的 HTML， CSR 的 HTML 是“客户端生成的”， 需要 直接从 0 开始创建DOM，没有可接管的内容。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="注水的作用"&gt;注水的作用
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;注水不是重新渲染，而是对比已有 HTML 和虚拟 DOM，找到匹配点， 然后 &lt;strong&gt;“在已有 DOM 上挂载 JS 逻辑”&lt;/strong&gt; ；这样可以省掉客户端从 0 渲染，页面加载速度更快。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="注水顺序"&gt;注水顺序：
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;【初始化客户端数据】 创建响应式状态 或 store 状态 【还没有对比DOM结构】&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;执行组件的 initData() 或 data: { &amp;hellip; }&lt;/li&gt;
&lt;li&gt;恢复 Vue 的 data()，React 的 useState()、Redux 状态等&lt;/li&gt;
&lt;li&gt;或读取 window.&lt;strong&gt;INITIAL_DATA&lt;/strong&gt; 来还原服务端数据&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;【对比DOM】&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对比 客户端 JS 渲染出来的“虚拟 DOM” 和 页面中已经由服务端生成并送到客户端的“真实 DOM” （不一致可能会触发警告或 DOM patch）&lt;/li&gt;
&lt;li&gt;如果不一致，框架会：
&lt;ul&gt;
&lt;li&gt;报 warning（如 Vue 的 hydration mismatch）&lt;/li&gt;
&lt;li&gt;或直接 patch DOM（丢掉 SSR 性能）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;🔁 这是为了让“JS 状态”与“页面结构”达成一致。&lt;/li&gt;
&lt;li&gt;⚠️ 特别注意：不是所有属性都“必须对比”
&lt;ul&gt;
&lt;li&gt;一些如 data-* 属性、非交互性属性，或者某些 aria-* 辅助属性，框架可能会有容忍度。&lt;/li&gt;
&lt;li&gt;但关键属性（如：id、class、key、绑定属性、样式、children 顺序）都必须对比且一致，否则注水可能失败。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;【挂载事件监听器】&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="ssr的优缺点"&gt;SSR的优缺点
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;优点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首屏加载速度快：由于服务器已经生成了完整的HTML页面，因此客户端可以直接显示这个页面，无需等待JavaScript加载和执行。&lt;/li&gt;
&lt;li&gt;SEO友好：搜索引擎爬虫可以很好地解析由服务器生成的HTML页面内容，有利于SEO优化。&lt;/li&gt;
&lt;li&gt;适合复杂页面：对于包含大量数据、需要复杂计算的页面，SSR可以更好地处理并减少客户端的负载。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;缺点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;服务器压力大：对于每个请求，服务器都需要重新渲染页面，这可能导致服务器压力过大。&lt;/li&gt;
&lt;li&gt;开发限制：SSR要求开发者在编写Vue组件时，需要考虑到服务器端和客户端环境的差异，不能过度依赖客户端环境。&lt;/li&gt;
&lt;li&gt;调试困难：SSR的调试过程相对复杂，需要同时考虑到服务器端和客户端的日志和错误信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="-diff-注水-vs-挂载-vs-重新渲染"&gt;🚧 Diff 注水 vs 挂载 vs 重新渲染
&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;动作&lt;/th&gt;
&lt;th&gt;会创建 DOM？&lt;/th&gt;
&lt;th&gt;会挂载事件？&lt;/th&gt;
&lt;th&gt;用在哪&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SSR&lt;/td&gt;
&lt;td&gt;服务器渲染HTML&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;首屏&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hydration注水&lt;/td&gt;
&lt;td&gt;接管 HTML&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;首屏&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSR 挂载&lt;/td&gt;
&lt;td&gt;浏览器全量渲染&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;二次跳转、非首屏&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</description></item></channel></rss>