Vue 知识篇(2):浅谈Vue中的DOM与VNode

DOM,相信无论是初入前端的小伙伴还是已经沉浸在前端多年的大佬,都对这个东西不会很陌生,但是对于这个东西很多人只知其然不知其所以然,所以今天我们来刨析一个这个跟多我打了很多年交道的老朋友。

一、DOM 节点的基本概念

1.1 DOM 节点的基本概念

  DOM,英文全称是[Document Object Model],翻译过来就是文档对象模型,DOM节点是构成网页文档结构的基本单位,是 HTML/XML 文档中每个组成部分的对象表示,浏览器通过 DOM 节点构建文档的树形结构 DOM 树。

🧩 DOM节点就像乐高积木,想象你在玩乐高玩具:

  • 每个单独的乐高积木块就是一个DOM节点
  • 你把积木拼成房子、车子,就像DOM节点组成网页
  • 大积木可以套小积木,就像<div>里面可以放<p>

1.2 DOM主要节点类型

  在 DOM中,nodeType 的值用于区分不同类型的节点。这些值是预定义的常量,可以帮助开发者在操作 DOM 时准确地识别和处理不同类型的节点。

类型说明
元素节点1HTML 标签(如 <div><p>
属性节点2HTML 属性(如 class="title"
文本节点3元素内的文本内容
注释节点8HTML 注释(<!-- 注释 -->
文档节点9整个文档(document
文档类型节点10<!DOCTYPE html>
  1. 元素节点(Element Node)
  • :1
  • 说明:表示HTML标签,如 <div><p> 等。
  • 用途:当你需要操作HTML元素(如获取元素的属性、修改元素的内容等)时,可以通过检查节点类型是否为1来确定它是一个元素节点。
    在这里插入图片描述
  1. 属性节点(Attribute Node)
  • :2
  • 说明:表示HTML属性,如 class="title"
  • 用途:虽然在现代DOM操作中,属性通常通过元素的 attributes 属性来访问,但在某些情况下,你可能需要检查节点类型是否为2来确定它是一个属性节点。
  1. 文本节点(Text Node)
  • :3
  • 说明:表示元素内的文本内容。
  • 用途:当你需要获取或修改元素内的文本内容时,可以通过检查节点类型是否为3来确定它是一个文本节点。
  1. 注释节点(Comment Node)
  • :8
  • 说明:表示HTML注释,如 <!-- 注释 -->
  • 用途:在某些情况下,你可能需要处理HTML注释,例如在解析或生成HTML代码时,可以通过检查节点类型是否为8来确定它是一个注释节点。
  1. 文档节点(Document Node)
  • :9
  • 说明:表示整个文档,即 document 对象。
  • 用途:当你需要操作整个文档(如获取文档的根节点、设置文档的标题等)时,可以通过检查节点类型是否为9来确定它是一个文档节点。
  1. 文档类型节点(Document Type Node)
  • :10
  • 说明:表示文档类型声明,如 <!DOCTYPE html>
  • 用途:在某些情况下,你可能需要检查文档类型声明,例如在解析或生成HTML代码时,可以通过检查节点类型是否为10来确定它是一个文档类型节点。
    在这里插入图片描述

1.3 DOM 节点的核心属性

  在DOM中,所有节点都具有一些通用属性,这些属性可以帮助我们获取节点的基本信息和关系。
在这里插入图片描述

属性说明
nodeName节点名称(标签名大写)
nodeType节点类型(数字值)
nodeValue节点值(文本/注释节点才有)
childNodes所有子节点的集合
parentNode父节点
previousSibling前一个兄弟节点
nextSibling后一个兄弟节点

1 nodeName

  • 说明:节点名称。对于元素节点,它是标签名的大写形式;对于属性节点,它是属性名;对于文本节点,它是#text;对于注释节点,它是#comment;对于文档节点,它是#document
  • 用途:通过nodeName可以快速获取节点的类型或名称。

2 nodeType

  • 说明:节点类型,是一个数字值,表示节点的类型。常见的节点类型及其值如1.2所述
  • 用途:通过nodeType可以判断节点的类型,从而进行相应的操作。

3 nodeValue

  • 说明:节点的值。对于文本节点,它是文本内容;对于注释节点,它是注释内容;对于属性节点,它是属性值。其他类型的节点nodeValue通常为null
  • 用途:通过nodeValue可以获取或设置节点的值。

4 childNodes

  • 说明:一个NodeList对象,包含当前节点的所有子节点。
  • 用途:通过childNodes可以遍历当前节点的所有子节点,进行操作或查询。

5 parentNode

  • 说明:当前节点的父节点。如果当前节点是文档的根节点,则parentNodenull
  • 用途:通过parentNode可以获取当前节点的父节点,从而进行向上级的查询或操作。

6 previousSibling

  • 说明:当前节点的前一个兄弟节点。如果没有前一个兄弟节点,则为null
  • 用途:通过previousSibling可以获取当前节点的前一个兄弟节点,进行同级的查询或操作。

7 nextSibling

  • 说明:当前节点的后一个兄弟节点。如果没有后一个兄弟节点,则为null
  • 用途:通过nextSibling可以获取当前节点的后一个兄弟节点,进行同级的查询或操作。

1.4 DOM 节点的操作

  我们在讲DOM节点的类型时候讲过类型的值为9的节点叫文档节点(Document Node)documentDOM中的一个核心对象,它代表了整个HTML文档。通过document对象,可以访问和操作文档中的所有元素和属性。它是整个DOM树的根节点,是与页面内容交互的入口点。

方法描述
getElementById()通过 ID 获取单个元素
querySelector()通过 CSS 选择器获取第一个匹配元素
querySelectorAll()通过 CSS 选择器获取所有匹配元素
createElement()创建新的 HTML 元素
createTextNode()创建文本节点
createDocumentFragment()创建文档片段(性能优化用)
write()向文档流写入内容
addEventListener()添加事件监听器
removeEventListener()移除事件监听器
hasFocus()检查文档是否获得焦点

  关于DOM节点的操作我将列举其主要函数等,但是并不会深入探讨,现在前端的主流框架VUE等是数据驱动视图,通过document直接操作DOM违背了Vue 的设计原则。

现在框架中为什么不推荐直接操作 DOM?
  1. 违背响应式原则
    Vue 的响应式系统会自动跟踪数据变化并更新 DOM,直接操作 DOM 会导致视图与数据状态不同步。

  2. 破坏组件封装性
    直接操作其他组件的 DOM 可能引发不可预期的副作用,降低代码可维护性。

  3. 性能优化失效
    Vue 的虚拟 DOM(Virtual DOM)会高效批量更新真实 DOM,直接操作会绕过这一优化机制。

  4. SSR/跨平台兼容性问题
    服务端渲染(SSR)或非浏览器环境(如 Weex)中 document 对象不存在,直接操作会导致错误。

  5. DOM 操作是昂贵的
    每次修改 DOM(如修改样式、添加/删除节点),浏览器需要重新计算布局并重新绘制,这会消耗大量 CPU/GPU 资源。

<template>
  <div ref="myDiv">Hello Vue</div>
</template>

<script>
export default {
  mounted() {
    // 通过 $refs 访问而不是 document
    this.$refs.myDiv.textContent = 'Updated content';
  }
}
</script>

二、VNode

2.1 什么是 VNode?

  VNode全称为[Virtual Node],中文名称虚拟节点,是 Vue 用来描述真实 DOM 节点的 JavaScript 对象。它相当于真实 DOM 的轻量级"蓝图"。你可以把VNode想象成建筑师的"设计图纸",而真实DOM就是实际建好的房子。

2.2 VNode 的核心属性

属性类型说明
tagStringHTML标签名或组件名
dataObject包含class, style, attrs
childrenArray子VNode数组
textString文本节点的内容
elmDOM Element对应的真实DOM节点
keyString/Number用于Diff算法的唯一标识
  1. tag

    • 类型:String
    • 说明:
      表示 HTML 原生标签名(如 "div""span")或注册的组件名(如 "MyComponent")。
      • 原生标签会渲染为对应的 DOM 元素
      • 组件名会触发组件实例化流程
  2. data

    • 类型:Object
    • 说明:
      包含节点的配置数据,常用字段包括:
      {
        class: 'active',    // CSS 类名
        style: { color: 'red' },  // 行内样式
        attrs: { id: 'app' },     // HTML 特性
        on: { click: handler }    // 事件监听
      }
      
  3. children

    • 类型:Array<VNode>
    • 说明:
      当前节点的子节点数组,支持嵌套结构。特殊说明:
      • 空数组表示无子元素
      • 文本节点可用字符串直接表示(如 ['文本']
  4. text

    • 类型:String
    • 说明:
      专为文本节点设计的属性,与 tag 互斥。例如:
      { text: '纯文本内容' }  // 等效于 document.createTextNode()
      
  5. elm

    • 类型:DOM Element
    • 说明:
      在 patch 阶段由框架自动挂载,指向该 VNode 对应的真实 DOM 节点。开发者通常无需手动操作。
  6. key

    • 类型:String | Number
    • 说明:
      Diff 算法的核心优化标识,适用于:
      • v-for 列表渲染(避免就地复用问题)
      • 动态组件切换(强制触发生命周期)

2.3 为什么要使用VNode

  1. 性能优化

    • 减少直接操作DOM的次数
      直接操作DOM会触发浏览器重排和重绘,性能消耗较大。VNode通过在内存中操作虚拟DOM,批量处理真实DOM更新,减少渲染开销。
    • 高效的更新机制
      通过Diff算法(如Vue的Snabbdom)对比新旧VNode差异,仅更新必要的DOM节点,避免全量渲染。
  2. 提高开发效率

    • 声明式编程
      用声明式描述UI状态(如Vue模板/React JSX),替代手动操作DOM的命令式代码,提升可维护性。
    • 组件化开发
      支持将UI拆分为可复用组件(如.vue文件/React组件),简化复杂界面开发。
  3. 跨平台支持

    • 跨浏览器兼容
      基于JavaScript标准实现,不依赖特定浏览器API,兼容Chrome/Firefox/Safari等。
    • 跨平台扩展
      虚拟DOM可应用于:
      • 服务端渲染(SSR):如Nuxt.js/Next.js
      • 移动端:React Native/Weex
      • 桌面端:Electron
特性设计图纸(VNode)真实房子(DOM)
表示方式用纸上的线条和标注表示房子是实际建好的砖瓦结构
修改难度修改起来非常快(擦掉重画就行)修改代价高(拆墙重建很费劲)
成本成本低(就是一张纸)成本高(要用真实建筑材料)
性能优化减少直接操作DOM的次数,高效的更新机制直接操作DOM,性能较低
开发效率声明式编程,组件化开发,代码更简洁、易于维护手动操作DOM,代码复杂,维护成本高
跨平台支持跨浏览器兼容,支持服务器端和移动设备依赖于浏览器,不支持跨平台

2.4 VNode与DOM 的关系

   VNode 本质是一个普通的 JavaScript 对象,用来描述 DOM节点,它的作用是VUE在内存中维护一个虚拟 DOM树(由多个 VNode 组成),用于高效计算 DOM 的更新。

// 一个简单的 VNode 示例
{
  tag: 'div',          // 标签名
  props: { class: 'container' },  // 属性(如 class, id)
  children: [          // 子节点
    { tag: 'p', children: 'Hello World' }
  ]
}

// 对应的真实DOM节点
<div class="container">
  <p>Hello World</p>
</div>

三、VNode 的创建过程

3.1 VNode 如何变成真实 DOM

Vue 的渲染流程:

  • 模板编译:Vue 模板(如 .vue 文件)会被编译成 渲染函数(render function)。

  • 生成 VNode:渲染函数执行后,返回一个 VNode 树(虚拟 DOM)。

  • Diff 比对:当数据变化时,Vue 会生成新的 VNode,并和旧的 VNode 进行对比(Diff 算法)。

  • 更新 DOM:只修改真正变化的部分(避免全量更新 DOM,提高性能)。
    在这里插入图片描述

3.2 手动创建

在深入探讨之前,我们需要先了解 Render 函数 的概念。Render 函数 是一个接收 createElement 方法(通常简写为 h)作为参数的函数,它返回一个虚拟 DOM 节点(VNode),用于描述组件的渲染结构。

createElement(即 h 函数)是 Vue 渲染机制的核心,专门用于创建 VNode。其有三个接收参数标签名(必需)数据对象(可选)子节点(可选)基本语法如下:

// HTML 标签
createElement('div')

// 组件
createElement(MyComponent)

// 文本子节点
createElement('div', 'hello world')

// 数组子节点
createElement('div', [
  createElement('span', 'hello'),
  createElement('span', 'world')
])

关于 Render 函数与 createElement 相关的知识点也是非常的密集和重要,因此放在同一个篇章肯定是讲不完了,所以后续会详细的讲解一些关于这两个的使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值