Nuxtjs中,举例子一篇文章讲清楚:水合sop

水合的意思

水合:在客户端,将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全局变量中获取值,
相关推荐
pas1368 分钟前
28-mini-vue customRender
前端·javascript·vue.js
REDcker12 分钟前
web 端 H265 软解码实现原理与使用说明
前端·音视频·播放器·h265·解码·软解码
倚栏听风雨16 分钟前
深度拆解:从 npm install 到手写一个全局 CLI 工具
前端
攀登的牵牛花18 分钟前
前端向架构突围系列 - 架构方法(二):UML前端建模的一般方法和工具
前端·前端框架·uml
代码猎人18 分钟前
Object.assign和扩展运算符是深拷贝还是浅拷贝,两者有什么区别
前端
秋天的一阵风19 分钟前
🎥解决前端 “复现难”:rrweb 录制回放从入门到精通
前端·javascript·开源
意法半导体STM3219 分钟前
【文末送NUCLEO-G431RB】一文说明白STM32G4双Bank启动与升级 LAT1596
前端·数据库·stm32·单片机·嵌入式硬件·mcu·stm32开发
前端一课19 分钟前
分享:基于Next.js的企业级提示词AI平台
前端
小高00721 分钟前
🔥「从零到一」我用 Node BFF 手撸一个 Vue3 SSR 项目(附源码)
前端·javascript·vue.js
SailingCoder23 分钟前
AI 流式对话该怎么做?SSE、fetch、axios 一次讲清楚
前端·javascript·人工智能·ai·node.js