vue2.0的虚拟dom和diff算法

本文深入探讨了虚拟DOM的概念及其在更新DOM时的作用,重点介绍了Diff算法的核心——最小量更新和键值(key)的重要性。通过h函数创建虚拟节点,并展示了如何使用patch函数将虚拟节点转换为真实DOM。同时,文章还讨论了手动实现h函数和Diff算法的准备工作,强调了key在决定节点是否为同一节点时的关键作用,以及无key和有key情况下的不同更新策略。
摘要由CSDN通过智能技术生成

1介绍

1.1 diff算法的目的就是:精选化比对前后变化,最小量更新

1.2 虚拟dom就是:用js对象描述DOM的层次结构,dom中的一切属性都在虚拟dom中有对应的属性

在这里插入图片描述

diff是发生在虚拟dom中的,也就是这个js对象,前后比对变化

在这里插入图片描述

2 h函数

  • h 函数用来产生虚拟节点(vnode),但不会产生真正的标签
  • 比如这样调用 h 函数

h('a', { props: { href: 'http://www.atguigu.com' } }, '尚硅谷')

将得到这样的虚拟节点: 

{ "sel": "a", "data": { "props": { "href": "http://www.atguigu.com" } }, "text": "尚硅谷" }

它表示的真正的 DOM 节点:

<a href="http://www.atguigu.com">尚硅谷</a>

一个虚拟节点的所有属性

{
  children: undefined, // 子节点,undefined表示没有子节点
  data: {}, // 属性样式等
  elm: undefined, // 该元素对应的真正的DOM节点,undefined表示它还没有上树
  key: undefined, // 节点唯一标识
  sel: 'div', // selector选择器 节点类型(现在它是一个div)
  text: '我是一个盒子' // 文字
}

使用patch函数,将虚拟节点生成真实的节点

import { init } from 'snabbdom/init'
import { classModule } from 'snabbdom/modules/class'
import { propsModule } from 'snabbdom/modules/props'
import { styleModule } from 'snabbdom/modules/style'
import { eventListenersModule } from 'snabbdom/modules/eventlisteners'
import { h } from 'snabbdom/h' // helper function for creating vnodes

// 创建出 patch 函数
const patch = init([
  classModule,
  propsModule,
  styleModule,
  eventListenersModule
])

// 创建虚拟节点
const myVNode1 = h(
  'a',
  { props: { href: 'http://www.baidu.com', target: '_blank' } },
  '百度一下就知道'
)

const myVNode2 = h('div', { class: { box: true } }, '我是一个盒子')
// 让虚拟节点上树
const container = document.getElementById('container')
// patch(container, myVNode1)
patch(container, myVNode2)

嵌套使用h函数,将会产生虚拟dom树

//比如这样嵌套使用h函数
h('ul', {}, [
  h('li', {}, '牛奶'),
  h('li', {}, '咖啡'),
  h('li', {}, '可乐')
])
//得到这样的虚拟dom树
{
  "sel": "ul",
  "data": {},
  "children": [
    { "sel": "li", "data": {}, "text": "牛奶" },
    { "sel": "li", "data": {}, "text": "咖啡" },
    { "sel": "li", "data": {}, "text": "可乐" }
  ]
}

3. 手写h函数

vnode.js

将传入的参数组合成对象返

/**
 * 产生虚拟节点
 * 将传入的参数组合成对象返回
 * @param {string} sel 选择器
 * @param {object} data 属性、样式
 * @param {Array} children 子元素
 * @param {string|number} text 文本内容
 * @param {object} elm 对应的真正的dom节点(对象),undefined表示节点还没有上dom树
 * @returns 
 */
export default function(sel, data, children, text, elm) {
  const key = data.key;
  return { sel, data, children, text, elm, key };
}

4. 手写diff算法准备
4.1 diff算法原理
最小量更新,key很关键。key是这个节点的唯一标识,告诉diff算法,在更改前后它们是同一个DOM节点。

只是同一个虚拟节点,才进行精细化比较(往ul中的 li 添加 li),否则就是暴力删除旧的、插入新的(ul中的li 换到在 ol 中去)

问题: 如何定义是同一个虚拟节点
答:选择器相同且key相同

只进行同层比较,不会进行跨层比较。即使是同一片 虚拟节点,但是跨层了,diff就是暴力删除旧的,然后插入新的

有key的话,先从开始新老的虚拟节点的开始位置 开始遍历,如果两个节点的标签和key相同则进行下一轮遍历,如果发现两个节点不相同,则从末尾开始遍历对比。

1、

 最后如果多出了新的节点,会找到对应的位置,并在patch函数中,旧节点传入null,实现新节点的挂载。

2、 

最后如果是多出了旧的节点,则将其卸载。

3、

 这种情况会创建一个新的数组,然后从旧节点中将标签和key跟新节点相同的节点push进新的数组,没有的节点就卸载,少的节点就挂载

如果不使用key,则尽可能的服用和修改相同的元素

如果使用key,则会基于key的变化重新排列元素的顺序,并会销毁key不存在的元素

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值