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全局变量中获取值,
相关推荐
修己xj6 分钟前
我用 AI 给自己写了一个 Markdown 编辑器,然后把它开源了
前端
FRing0323 分钟前
vue2干货总结2(v-bind、v-for、v-model、指令修饰符、v-bind操作class和style)
开发语言·前端·vue.js·vue2
幸运小猿43 分钟前
模拟慢上传-处理超时重试image 丢失线上问题
前端·javascript·html
moshuying2 小时前
网购能买Token了!
前端
h_65432102 小时前
echarts实现柱状图加一条横线
前端·echarts
风骏时光牛马3 小时前
生成一个后端标题,只需要一个标题,不需要其他的任何文本。
前端
子非鱼@Itfuture3 小时前
关于前后端联调中登录状态/登录信息失效?
java·前端
郭小李3 小时前
openFeign入门
java·开发语言·前端
馒头不想说话3 小时前
AI当运营,帮你省人力
前端
梦曦i3 小时前
uni-app x下自研架构的工程化突围
前端·架构·uni-app x