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全局变量中获取值,
相关推荐
IT_陈寒14 小时前
为什么我的JavaScript异步代码总是不按顺序执行?
前端·人工智能·后端
索西引擎14 小时前
【React】useState 状态更新机制:批量更新策略与“异步“错觉的深层解析
前端·react.js·前端框架
慢功夫15 小时前
💡第五篇:VSCode插件是如何与主进程通信的?
前端·visual studio code
锋行天下15 小时前
打造企业内部知识库系统RAG全栈项目
前端·后端·架构
竹林81815 小时前
用 ethers.js 连接 MetaMask 实现钱包登录:一个真实项目中的完整踩坑记录
前端·javascript
虚惊一场15 小时前
在浏览器里跑 Prettier:格式化 Markdown 的四个难点
前端·javascript·vue.js
前端炒粉15 小时前
手撕小汇总
java·前端·javascript
r_oo_ki_e_15 小时前
vue快速入门
前端·vue.js
虚惊一场16 小时前
把 CodeMirror 6 调教成 Markdown 编辑器:扩展、装饰与门面
前端·javascript·vue.js
lauo16 小时前
掌心核爆:iQOO首款小平板搭载2nm骁龙8E6,开启AI原生计算的移动终端新纪元
前端·人工智能·智能手机·重构·电脑·ai-native