菜单

DOM 管理

相关源文件

本页面介绍了 Svelte 如何管理 DOM,包括模板创建、属性管理、事件处理和绑定系统。它涵盖了根据状态变化高效更新 DOM 的运行时机制。有关组件生命周期的信息,请参阅 组件生命周期

概述

Svelte 采用直接 DOM 操作方法,而不是虚拟 DOM。在编译期间,Svelte 会分析组件并生成优化的 JavaScript,从而实现有针对性的 DOM 更新。DOM 管理系统由几个相互关联的子系统组成,它们协同工作以高效地渲染和更新组件。

来源: packages/svelte/src/internal/client/dom/elements/attributes.js packages/svelte/src/internal/client/dom/elements/events.js packages/svelte/src/internal/client/dom/elements/bindings/input.js

模板创建和 DOM 构建

Svelte 将组件编译成使用优化模板字符串创建 DOM 元素的 JavaScript。这种方法可以实现高效的初始渲染和精确的更新。

模板创建

模板使用 $.template() 函数创建,该函数将静态 HTML 字符串编译成可重用的片段工厂。调用此工厂时,它会创建可操作并插入到文档中的 DOM 节点。

来源: packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js87-122 packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js4

元素遍历和操作

在创建模板后,Svelte 使用各种函数来遍历和操作 DOM

  • $.first_child() - 获取元素的第一个子节点
  • $.sibling() - 获取同级元素
  • $.child() - 获取子节点
  • $.next() - 在初始化期间移动到下一个节点
// Example of DOM traversal pattern
var fragment = root();
var main = $.sibling($.first_child(fragment), 2);
var h1 = $.child(main);
var text = $.child(h1, true);

来源: packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js8-10

静态内容与动态内容

Svelte 通过区分静态和动态内容来优化渲染

  1. 静态内容 - 不会改变的元素直接包含在模板中
  2. 动态内容 - 会改变的元素通过 JavaScript 添加

例如,在此模板中

var root = $.template(`<header><nav>...</nav></header> <main>...</main>`, 3);

静态元素创建一次,而动态内容则通过函数调用单独更新。

来源: packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js4 packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js10-173

属性管理

Svelte 提供了一个全面的系统来管理元素属性、属性以及类和样式等特殊情况。

设置属性

$.set_attribute() 函数用于在 DOM 元素上设置属性。它处理各种特殊情况

  • 布尔属性(为 true 时仅需要名称)
  • 属性与元素的属性(有些值需要设置为属性)
  • 特殊属性,例如水合期间的 src

来源: packages/svelte/src/internal/client/dom/elements/attributes.js156-194 packages/svelte/src/internal/client/dom/elements/attributes.js268-450

类名和样式管理

类名和样式属性会得到特殊处理

  • $.set_class() - 处理类名属性更新、作用域样式的 CSS 哈希添加和类名指令
  • $.set_style() - 管理样式属性更新和样式指令

clsx() 实用函数有助于将各种类名值(字符串、对象、数组)转换为正确的类名字符串。

来源: packages/svelte/src/internal/client/dom/elements/class.js1-51 packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js156-271

特殊元素处理

Svelte 对特定元素有特殊处理

元素特殊处理
<input>值、选中状态、文件、默认值
<textarea>值、子内容
<select>选项选择、选项值
<option>选中状态、值处理
自定义元素属性与元素的属性、事件处理

例如,对于 <input> 元素,Svelte 会处理重置默认值以及根据绑定更改进行更新。

来源: packages/svelte/src/internal/client/dom/elements/attributes.js33-64 packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js176-211

事件处理

Svelte 提供了一个复杂的事件处理系统,支持委托和正确的事件冒泡。

事件监听器

事件使用 $.create_event() 函数附加到元素上,该函数封装了事件处理程序以管理响应式上下文和事件冒泡。

来源: packages/svelte/src/internal/client/dom/elements/events.js49-88

事件委托

为提高效率,Svelte 在可能的情况下使用事件委托。$.delegate() 函数注册要由更高级别处理的事件类型,从而减少了事件监听器的数量。

来源: packages/svelte/src/internal/client/dom/elements/events.js132-140 packages/svelte/src/internal/client/dom/elements/events.js147-284

事件传播

Svelte 通过 $.handle_event_propagation() 函数谨慎地管理事件传播。该系统

  • 处理通过组件层级的事件冒泡
  • 在传播过程中正确管理 currentTarget
  • 防止嵌套 Svelte 应用中的无限循环
  • 将事件与响应式上下文隔离,以防止意外的副作用

来源: packages/svelte/src/internal/client/dom/elements/events.js147-284

连接系统

Svelte 的绑定系统将 DOM 元素连接到组件状态,创建双向数据流。

值绑定

对于文本输入、文本区域和其他具有 value 属性的元素,$.bind_value() 函数建立双向连接

  1. 当绑定的变量发生变化时更新 DOM
  2. 当用户与元素交互时更新绑定的变量

来源: packages/svelte/src/internal/client/dom/elements/bindings/input.js11-85

复选框和单选按钮绑定

复选框和单选按钮使用专门的绑定函数

  • $.bind_checked() - 用于简单的复选框绑定
  • $.bind_group() - 用于复选框组和单选按钮组

来源: packages/svelte/src/internal/client/dom/elements/bindings/input.js86-251

Select 元素绑定

选择元素需要使用 $.bind_select_value()$.select_option() 函数进行特殊处理,这些函数根据绑定的值管理选项的选取。

来源:packages/svelte/src/internal/client/dom/elements/bindings/select.js1-147

媒体元素绑定

媒体元素(音频/视频)对诸如 currentTimevolumepaused 等属性具有专门的绑定。这些绑定通常使用动画帧或事件来同步元素状态与组件状态。

来源:packages/svelte/src/internal/client/dom/elements/bindings/media.js1-218

尺寸和窗口绑定

对于元素尺寸和窗口属性,Svelte 提供了专门的绑定函数。

  • $.bind_resize_observer() - 用于元素尺寸
  • $.bind_window_scroll() - 用于窗口滚动位置
  • $.bind_window_size() - 用于窗口尺寸

这些函数使用适当的浏览器 API(如 ResizeObserver)进行高效更新。

来源:packages/svelte/src/internal/client/dom/elements/bindings/size.js1-118 packages/svelte/src/internal/client/dom/elements/bindings/window.js1-66

水合作用

水合(Hydration)是将客户端 JavaScript 附加到服务器渲染的 HTML 的过程。Svelte 的水合系统可确保从服务器到客户端渲染的无缝过渡。

注水过程

在水合期间

  1. 会设置 $.hydrating 标志来控制行为
  2. DOM 节点与预期结构匹配
  3. 属性和事件监听器被附加
  4. 组件状态被初始化

来源:packages/svelte/src/internal/client/dom/elements/attributes.js158-177 packages/svelte/src/internal/client/dom/elements/events.js26-47

特殊水合情况

某些元素在水合期间需要特殊处理。

  • 表单元素:需要保留默认值
  • 媒体元素:需要避免不必要的网络请求
  • 事件:可能需要重播加载和错误事件

Svelte 提供了像 $.remove_input_defaults()$.replay_events() 这样的函数来处理这些情况。

来源:packages/svelte/src/internal/client/dom/elements/attributes.js33-64 packages/svelte/src/internal/client/dom/elements/events.js26-47

总结

Svelte 的 DOM 管理系统是一套全面的实用程序,可高效地创建、更新和管理 DOM 元素。通过使用直接的 DOM 操作而非虚拟 DOM,Svelte 以最小的运行时代码实现了高性能。该系统仔细处理特殊情况、事件委托,并提供了一个强大的绑定系统,将 DOM 元素连接到组件状态。

Svelte 方法的主要优点是

  1. 效率:直接 DOM 操作,无虚拟 DOM 开销
  2. 精度:只更新需要更改的部分
  3. 丰富的功能:对属性、事件和绑定的全面支持
  4. 服务器渲染:无缝水合服务器渲染内容

这些功能使 Svelte 能够提供强大而轻量级的组件模型,最大限度地减少运行时开销,同时提供丰富的开发体验。