水合的意思
水合:在客户端,将vue组件和服务器渲染的静态html 进行"激活"的过程,使其成为完全交互式的单页应用(SPA)。
sop(工作流程)
1、服务器渲染:Nuxt在服务器生成页面的静态HTML。
2、发送到客户端:html+vue组件的js代码一起发送到浏览器。
3、水合过程:Vue在客户端接管静态html,将其与Vue组件实例关联,添加事件监听器,恢复响应式状态。
代码例子
计数器
整个Counter.vue都是在服务器端渲染的,然后在客户端进行水合(也就是所谓的激活的意思)。
html
<!-- components/Counter.vue -->
<template>
<div>
<p>计数:{{ count }}</p>
<button @click="increment">增加</button>
</div>
</template>
<script setup>
const count = ref(0)
const increment = () => count.value++
</script>
sop(计数器水合过程)
- 服务器渲染:
<p>计数:0</p>(静态 HTML) - 发送到客户端:
HTML + Vue组件代码 - 水合:
Vue找到这个DOM元素,将count响应式变量绑定,添加点击事件。 - 结果:按钮可以交互,数字可以更新。
服务器
服务器渲染以下代码:
html
<-- Nuxt 服务器渲染的完整 html -->
<div>
<p>计数:0</p>
<button>增加</button>
</div>
服务器会做这些动作:
- 1、执行
<script setup>中的代码 - 2、
count.value初始化为0 - 3、使用这个值渲染模版
- 4、生成包含
当前状态的静态html - 5、不渲染任何事件监听器(
@click)
发送到客户端的内容
发送到客户端的内容如下:
html
<!DOCTYPE html>
<html>
<head>...</head>
<body>
<!-- 服务器渲染的静态 HTML -->
<div>
<p>计数: 0</p>
<button>增加</button>
</div>
<!-- 打包的 JavaScript -->
<script src="/_nuxt/entry.js"></script>
<script>
// 包含 Counter 组件的 Vue 代码
const Counter = {
setup() {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
},
template: `<div>...</div>`
}
</script>
</body>
</html>
客户端
然后客户端进行水合,
- 1、加载html:浏览器显示静态的计数"0"
- 2、加载js :下载包含
Vue和组件代码的js - 3、进行水合激活:
水合激活,我写步骤在下面
js
// Vue 内部执行:
// 1. 找到 <div> 元素
// 2. 将 count 响应式变量绑定到 DOM
// 3. 给按钮添加 click 事件监听器
// 4. 建立虚拟 DOM 与真实 DOM 的关联
组合例子 ------ 条件渲染
html
<!-- components/Example.vue -->
<template>
<div>
<!-- 这个是在服务器渲染的 -->
<p>服务器时间: {{ serverTime }}</p>
<!-- 这个是在客户端水合后渲染的 -->
<p v-if="isClient">客户端时间: {{ clientTime }}</p>
<button @click="update">更新</button>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
// 这部分代码服务器和客户端都会执行
const serverTime = ref(new Date().toISOString())
// 这部分只在客户端执行
const isClient = typeof window !== 'undefined'
const clientTime = ref('')
onMounted(() => {
if (isClient) {
clientTime.value = new Date().toISOString()
}
})
const update = () => {
clientTime.value = new Date().toISOString()
}
</script>
渲染出来的结果对比:
服务器渲染的html:
html
<div>
<p>服务器时间: 2024-01-15T10:30:00.000Z</p>
<!-- v-if="false",所以没有这个元素 -->
<button>更新</button>
</div>
客户端水合后:
html
<div>
<p>服务器时间: 2024-01-15T10:30:00.000Z</p>
<p>客户端时间: 2024-01-15T10:30:05.123Z</p>
<button>更新</button>
</div>
<!-- 现在按钮有点击事件了 -->
| 部分 | 服务器渲染 | 客户端渲染 |
|---|---|---|
| 初始 HTML 结构 | ✅ | ❌ |
初始数据 (count: 0) |
✅ | ❌ |
事件监听器 (@click) |
❌ | ✅ |
后续更新 (count++) |
❌ | ✅ |
| 响应式系统 | ❌(只是静态值) | ✅(激活响应式) |
sop(水合过程)
水合过程:
- 1、服务器端 :读环境变量,然后,渲染到
{{ config.public.apiBase }}html当中。 - 2、客户端水合 :
useRuntimeConfig()从window全局变量中获取值,