Vdom vNode

Vdom

  • vdom 是实现 vue 和 React 的基石 ,通过 JS 模拟 DOM 结构(vnode
  • 虚拟dom 指将页面上的所有 dom 元素以对象的形式保存于内存中
  • 包含了标签名、属性、子元素等信息;
  • 通过新旧 vnode 对比,得出最小的更新范围,最终对需要更新的 dom 进行实际操作;

模拟 Vdom 结构:

<div id="div1" class="container">
  <p>title</p>
  <ul style="font-size: 20px">
    <li>hello</li>
  </ul>
</div>
const vdom = {
  tag: 'div',
  props: {
    className: 'container',
    id: 'div1'
  },
  children: [
    {
      tag: 'p',
      children: 'title' // 文本内容
    },
    {
      tag: 'ul',
      props: {
        style: 'font-size:20px'
      },
      children: [
        {
          tag: 'li',
          children: 'hello' // 文本内容
        }
      ]
    }
  ]
}

使用 snabbdom 了解 vdom 原理:

  • html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
  </head>
  <body>
    <div id="container"></div>
    <button id="btn-change">change</button>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-class.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-props.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-style.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-eventlisteners.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/h.js"></script>
    <script src="./snabbdom.js"></script>
  </body>
</html>
  • snabbdom.js 文件: 定义 h 函数,最终返回 vnodevnode 本身也是函数,返回一个对象即 JS 模拟的 DOM 结构
const h = window.snabbdom.h
const container = document.getElementById('container') // 挂载 Vdom 的容器

h 函数 和 vnode 函数内部

function h() {
  // ... do something
  return vnode(sel, data, children, text, undefined)
}

function vnode(sel, data, children, text, elm, key) {
  let key = data === undefined ? undefined : data.key
  return { sel, data, children, text, elm, key }
}
  • vnode
    • seltag 标签,如 div p a span
    • data - 属性 attr、children - 子节点
    • elm - vnode 对应 DOM 真实元素
    • text - 文本
    • key 用于 diff 算法对比新旧 dom 实现更新和重新渲染
let vnode = h(
  'ul#list', // 标签ul id=list
  {}, // 属性
  [
    h(
      'li.item', // li标签 class = item
      {},
      'Item 1' // 内容(text)
    ),
    h('li.item', {}, 'Item 2')
  ]
)
  • snabbdom.init : init 会初始化生命周期,初始化完成后会返回 patch 函数
  • patch: 函数执行结果会将 vdom 渲染成真实 dom
    • 传入的第一个参数是容器会创建一个空的 vnode,并关联 elm(真实DOM元素)
    • 传入的参数都是 vNode,则会进行 diff 算法比较
const patch = snabbdom.init([
  snabbdom_class, // 提供了对元素类名操作的功能,包括添加、删除和切换类名
  snabbdom_props, // 用于处理元素的属性,包括属性的设置、获取和删除。
  snabbdom_style, // 用于处理元素的样式,包括样式的设置和获取。
  snabbdom_eventlisteners // 用于处理元素的事件监听器,包括添加和删除事件监听器,以及触发事件。
])
patch(container, vnode) // 初次渲染:虚拟DOM渲染到HTML

// 模拟更新
document.getElementById('btn').addEventListener('click', () => {
  const newVnode = h('ul#list', {}, [
    h('li.item', {}, 'Item 1'),
    h('li.item', {}, 'Item 3'),
    h('li.item', {}, 'Item 2')
  ])
  patch(vnode, newVnode) // 新的虚拟的DOM
  vnode = newVnode // patch 之后,应该用新的覆盖现有的 vnode
})

diff

  • 最开始会遍历 tree1 和 tree 2 继续比较,然后进行 排序,导致时间复杂度到达 O^3;
  • 后来进行了优化将时间复杂度优化到了O(n)
    • 只比较同一层级,如果 tag 不相同直接删除重建
    • 如果 tagkey 都相同不再深度比较,而是直接进行一个新旧 vnode 的比较
  • patch 函数中新旧 vNode 对比的大致原理:
    1. 首先将新的vnode关联到旧的vnode所对应的 elm(真实Dom)
    2. 然后进行childrentext 的对比,分为4个方向
    3. 如果oldVnode含有childrennewVnodetext:移除旧vnode,设置新text
    4. 如果oldVnodetextnewVnode含有children:创建新vnode,移除旧text
    5. 如果oldVnodetextnewVnode也为text:移除旧的text设置新text
    6. 如果oldVnode含有childrennewVnode也含有children:每一个框架的算法都不同,共性都依赖于 key 进行计算
    7. 旧的开始和旧的开始,旧的结束和旧的结束,旧的开始和新的结束,旧的结束和新的开始4个维度进行计算

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值