Javascript is required
[翻译]深入了解Nuxt Server Components

Nuxt Server Components

翻译:Claude

原文:https://speakerdeck.com/wattanx

PDF

Deep_dive_into_Nuxt_Server_Components.pdf 5708427

PPT

自我介绍

  • STORES 株式会社软件工程师
  • 喜欢猫,居住在大阪
  • Nuxt 项目贡献者,Nuxt Bridge 维护者

目录/大纲

  1. Nuxt 的渲染模式和挑战
  2. Nuxt Server Components 简介
  3. Nuxt Server Components 的应用方法
  4. 动手学习 Nuxt Server Components
  5. 与其他类似技术的区别

开场

本次演讲目标

让大家了解 Nuxt Server Components:

  • 能做什么
  • 解决什么问题
  • 工作原理是什么
  • 与其他技术有何不同

Nuxt 的渲染模式

支持的渲染模式

  • 客户端渲染(Client-Side Rendering)
  • 服务端渲染(Server-Side Rendering)

客户端渲染的特点

  • 动态获取数据
  • 使用 JavaScript 将获取的数据显示在页面上
  • 使用 JavaScript 实现页面跳转

客户端渲染流程

  1. 浏览器请求页面
  2. 服务器返回空 HTML
  3. 浏览器下载 JavaScript 代码
  4. 执行 JavaScript 进行渲染
        graph TD
  A[HTML] --> B[GET /bundle.js]
  B --> C[JS]
  C --> D[Render]

      
        graph LR
  A[空的 HTML 被下载] --> B[JS 被下载]
  B --> C[渲染]

      

客户端渲染的问题

  1. 性能问题
    • 需要等待浏览器下载、解析和执行 JavaScript
    • 内容显示时间较慢
  2. 搜索引擎优化问题
    • 对爬虫不友好
    • 虽然有些爬虫可以解析 JavaScript,但仍有局限

服务端渲染(SSR)

  • 解决了客户端渲染的问题
  • 服务器返回完整渲染好的 HTML 给浏览器

服务端渲染流程

  1. 浏览器请求页面
  2. 服务器返回渲染好的 HTML
  3. 浏览器下载 JavaScript
  4. 进行水合(Hydration)
        sequenceDiagram
    participant Browser
    participant Server

    Browser->>Server: GET /
    Server-->>Browser: HTML
    Browser->>Server: GET /bundle.js
    Server-->>Browser: JS
    Browser->>Browser: Hydration
    Note over Browser: 绑定事件监听器<br/>建立响应式数据
    Browser->>Browser: 交互事件
    Browser->>Browser: 重新渲染
    Note over Browser: 客户端渲染

      

Hydration 解释

  • 服务器返回的 HTML 没有事件监听器
  • Hydration 是为 HTML 添加事件监听器的过程
        sequenceDiagram
    participant Server
    participant Browser
    participant Vue.js

    Server->>Browser: 发送服务端渲染的 HTML
    Browser->>Browser: 显示静态 HTML
    Browser->>Server: 请求 JavaScript 包
    Server->>Browser: 发送 JavaScript 包
    Browser->>Vue.js: 初始化 Vue.js 应用
    Vue.js->>Browser: 查找根 Vue 组件
    Vue.js->>Browser: 附加事件监听器和数据绑定
    Note over Browser,Vue.js: Hydration 完成
    Browser->>Vue.js: 交互事件
    Vue.js->>Browser: 更新 DOM
    Note over Browser,Vue.js: 客户端渲染

      

服务端渲染的遗留问题

  • bundle 大小增长问题

举例:使用 markdown 转 HTML 显示内容的场景

  • 代码在服务端和客户端都要执行(假设使用 SSR)
  • 原因:两边要执行得到相同的 DOM 结构才能进行 Hydration
  • 导致客户端 bundle 包含了大型库:
    • sanitize-html: 194.8KB (80.9KB gzipped)
    • marked: 35.9KB (11.2KB gzipped)

A:因为如果两者不执行相同的 DOM 结构,就无法进行 Hydration

Nuxt Server Components 介绍

有那么方便的机制吗?

概述

• 从Nuxt 3引入的功能

• 仍然是实验性的

• 可以创建仅在服务器上渲染的组件

主要特点

  • 只在服务器端执行,无需 Hydration
  • 不必要的 JS 代码不会打包到客户端
  • 不强制要求服务器
    • 可以在构建时预渲染所有 Server Components
    • 适用于完全静态的网站

性能提升

使用方法

  1. 文件扩展名改为 .server.vue
  2. 使用 NuxtIsland 组件
  3. 启用 experimental.componentIslands 配置

NuxtIsland 组件

  • Server Component 的基础设施
  • .server.vue 会被转换为使用 NuxtIsland 组件的代码
  • components/islands 目录下的组件会只在服务器端渲染

使用者可以不用 import 使用

或者从 #components 导入

(从实际路径导入尚未支持)

• 服务器组件的基础

.server.vue 将被转换为使用 Nuxtlsland 组件的代码

• 在 components/islands 中创建组件时,将成为只能在服务器上渲染的组件

静态部分称为岛屿(Island)

NuxtIsland 是在动态部分中嵌入静态内容的架构。

功能特性

Nuxt Server Components 可以实现的功能(部分摘录)

• 可以使用异步组件

• 可以嵌套服务器组件和客户端组件

• 服务器组件中,只有一部分可以进行 Hydration

通过使用 nuxt-client 指令,可以对 Server Component 的一部分进行 Hydration。

应用场景

适合使用的场景

  • 博客(大多数情况下不需要交互)
  • 定时显示的组件
    • 特定日期显示的组件
    • 避免敏感信息提前包含在客户端 bundle 中

不适合使用的场景

  • 频繁更新 props 的组件
    • 每次 props 变化都需要服务器请求

注意事项

  • Server Component 嵌套
    • 每个 Server Component 渲染都需要服务器请求
    • 嵌套会增加开销

工作原理详解

制作 Nuxt 服务器组件并解释其机制

最终完成的东西

  • 作为服务器组件基础的 NuxtIsland
  • 仅在服务器上渲染组件的机制
npm create vite-extra@latest app -- --template
ssr-vue-ts

Nuxt Server Components 的流(在服务器端渲染的情况下)

与传统渲染的差异

主要构成

Nuxt Server Components 的构成要素

• 渲染 Vue 组件的端点

• 在服务器上渲染 Vue 组件的处理

• 向渲染 Vue 组件的端点发送请求的组件

渲染 Vue 组件的端点

端点处理流程

从 URL 中提取组件名和 props 的处理

实际的 URL

/_nuxt_island/CodeExample_XdPPUtbPQW.json?props={"count":4}

从 URL 中提取组件名: :::highlight-text --- text: CodeExample --- :::

从查询参数中提取 :::highlight-text --- text: props: { count: 4 } --- :::

创建端点

  • 提取组件名称和 props 并填入 context
  • render 函数稍后说明

SSR 上下文

- 可以添加在 Server-Side Rendering 中想要使用的数据

- 在 Nuxt 中,也会使用请求事件

- 通过使用 useSSRContext 可以从组件内部访问

在服务器上渲染 Vue 组件的处理

创建组件名称与组件的映射。

创建动态渲染组件的组件

  • 获取要渲染的组件
  • 可以动态渲染组件

要在服务器上渲染 Vue 组件

使用 createSSRApp 和 renderToString

将动态组件渲染处理结合起来。

  • 动态渲染组件
  • 在服务器上渲染 Vue 组件

此render函数在/_nuxt_island执行

相当简化的代码

  • /_nuxt_island 发起请求
  • 由于在服务器上渲染在客户端,如果props发生变化则发起请求
  • 渲染HTML字符串 不进行Hydration

Nuxtlsland 组件

Nuxtlsland 会发送 /_nuxt_island 请求并渲染响应的 HTML 字符串。

组件级别的渲染机制已完成

这样下去,Server Component 的子组件就不能拥有交互式的组件。

通过使用插槽,可以将服务器组件和客户端组件嵌套在一起。

答:会变成普通的HTML字符串,因此不会进行水合(没有事件处理程序等信息)。

完全渲染的HTML字符串中如何插入Slot

Teleport

目标是在槽被内容替换的状态下进行渲染。

在构建时转换为自定义占位符。

• 在构建时将 转换为自定义占位符

• 用 Teleport 包围 Slot 的内容进行渲染→接下来是这里

• 在占位符部分插入 Slot 的内容

像这样改写

为了使Teleport部分更容易理解,代码如下所示。

•一个内置组件,可以将组件模板的一部分"传送"到位于该组件 DOM 层次结构之外的 DOM 节点

v-if 为 true 时插入到 body 元素中。

在服务器端渲染时,SSR Context 的 teleports 中会公开 Teleport 内的内容

(SSR Context 是传递给 renderToString 的第二个参数的对象)https://ja.vuejs.org/guide/scaling-up/ssr.htm|#teleports

当包含 Teleport 的 NuxtIsland 组件进行服务器端渲染时,可以获得如下的 Teleport 内的内容。

• 在构建时将 转换为自定义的占位符

• 用 Teleport 包围并渲染 Slot 的内容

• 将 Slot 的内容插入到占位符部分

在构建时转换为自定义占位符。

当 Nuxtlsland 被渲染时,data-island-uid 将被设置。

查看 uid=xXX,slot=default 可以知道应该插入到哪里。

因为找到了插入的位置,所以插入了 Teleport 的内容(在服务器渲染时用正则表达式替换)。

在服务器上完成的 HTML

指定的属性一致

Teleport之后

答:因为如果不在两者上执行并达到相同的 DOM 结构,就无法进行 Hydration。

与在服务器上完成的 HTML 相比

组件渲染机制

  • 使用 createSSRApp 和 renderToString
  • 动态组件渲染
  • Teleport 处理插槽内容

具体实现

详细的技术实现代码请参考: https://github.com/wattanx/mini-nuxt-sc

• 最小化 Nuxt 服务器组件

• 独立于 Nuxt 制作

• 使用 Vue 可以实现

与其他技术的对比

React Server Components

  • 在打包前预渲染
  • 不需要服务器也能运行
  • 渲染为特殊的 RSC Payload 格式(而不是 HTML)

Island Architecture

  • 在静态部分中嵌入动态(JS交互)部分的方法
  • 与 Nuxt Server Components 相反(在动态部分嵌入静态内容)

总结

主要优点

  • 在动态内容中嵌入静态内容的创新方案
  • 显著减少客户端 bundle 大小
  • 巧妙运用 Teleport 技术

未来展望

实验性功能尚在发展中:

  • 支持远程源渲染
  • 支持创建 Server Page
  • 支持 Lazy Server Component

因为还在实验阶段,所以请使用并反馈。