虚拟dom及diff算法之 —— h函数和diff函数

新虚拟dom和老虚拟dom进行diff算法(精细化比较),算出如何最小量更新,最后反映到真实dom上

diff是发生在虚拟dom上的

模板编译

虚拟dom如何产生 - 渲染函数(h函数)

h函数产生虚拟节点(vnode)

Dom节点:<a href="https://www.baidu.com">真实Dom</a>
调用h函数:h('a',{props:{href:"https://www.baidu.com"}},'真实Dom')
生成虚拟节点:{"sel":"a","data":{props:{href="https://www.baidu.com"}},"text:"真实Dom"}

虚拟节点有哪些属性

{
	children:{}, // 子元素
	data:{}, // 属性,样式
	elm:undefined, // 这个元素真正的dom节点,如果是undefined证明这个虚拟dom还没有上树
	key:undefined, // 节点唯一标识
	sel:"div", // 选择器
	text:"真实Dom" // 文字
}

使用patch函数让虚拟节点上树,一个容器只能让一个虚拟dom上树,除非h函数进行嵌套

import { init, classModule, propsModule, styleModule, eventListenersModule, h } from 'snabbdom'
// 创建patch函数
const patch = init([classModule, propsModule, styleModule, eventListenersModule])
// 创建虚拟节点
var myVnode1 = h('a', { props: { href: 'https://www.baidu.com', target: '_blank' } }, '真实Dom')
// 使用patch函数让虚拟节点上树
const container = document.getElementById('container')
// 一个容器只能让一个虚拟dom上树,除非h函数进行嵌套
patch(container, myVnode2)

h函数可以嵌套使用创建虚拟dom树

var myVnode3 = h('ul', {}, [h('li', '苹果'), h('li', '西瓜'), h('li', '香蕉'), h('li', '火龙果')])

第二个参数写不写不影响

var myVnode21 = h('div', {}, '我是一个盒子')
var myVnode22 = h('div', '我是一个盒子')

第三个参数如果再有一个子元素可以不要数组,如果有多个子元素需要使用数组

var myVnode3 = h('ul', {}, [
  h('li', {}, '苹果'),
  h('li', '西瓜'),
  h('li', [h('div', [h('p', '嘻嘻'), h('p', '哈哈')])]),
  h('li', h('span', '榴莲'))
])

h函数的重载(h函数的实现形式)

h('div')
h('div','文字'),
h('div',[])
h('div',h())
h('div',{},'文字'),
h('div',{},[])
h('div',{},h())

vnode函数

// 函数目的:将5个参数组合成一个对象返回
export default function (sel, data, children, text, elm) {
  return { sel, data, children, text, elm }
}

h函数的实现

import vnode from './vnode'
// 弱重载:函数重载功能没有实现,必须接收3个参数
// 形态1:h('div',{},'文字')
// 形态2:h('div',{},[])
// 形态3:h('div',{},h())
export default function (sel, data, c) {
  // 检查参数个数
  if (arguments.length !== 3) throw new Error('低配版,必须传入3个参数')
  // 检查参数c的类型
  if (typeof c === 'string' || typeof c === 'number') {
    // 形态1的h函数:文字
    return vnode(sel, data, undefined, c, undefined)
  } else if (Array.isArray(c)) {
    // 形态2的h函数:数组
    let children = []
    // 遍历c,收集children
    for (let i = 0; i < c.length; i++) {
      // c[i]必须是一个有sel属性的对象
      if (!(typeof c[i] === 'object' && c[i].hasOwnProperty('sel'))) throw new Error('传入的数组有项不是h函数')
      // 不用执行c[i],因为在调用h函数的时候已经执行了
      children.push(c[i])
    }
    // 循环结束,锁门children收集完毕了,可以返回带有children属性的虚拟节点了
    return vnode(sel, data, children, undefined, undefined)
  } else if (typeof c === 'object' && c.hasOwnProperty('sel')) {
    // 形态3的h函数:h函数(一个有sel属性的对象),即的唯一的children
    let children = [c]
    return vnode(sel, data, children, undefined, undefined)
  } else {
    throw new Error('传入的第三个参数不对')
  }
}

请添加图片描述

请添加图片描述

diff算法原理

最小量更新:key很重要,key是这个节点的唯一标识,告诉diff算法,在更改前后他们是同一个dom节点

只有是同一个虚拟dom节点,才能进行精细化比较,否则会暴力删除旧的,插入新的
如果定义是同一个虚拟节点:选择器和key都相同

只进行同层比较,不会进行跨层比较,而是暴力删除旧的,插入新的

diff不是那么无微不至,但是2,3在实际的vue开发中,基本不会遇见

请添加图片描述

创建节点时,所有子节点都是需要递归创建的

patch函数的实现

// init主要是组装函数模型的
const patch = init([classModule, propsModule, styleModule, eventListenersModule])

虚拟dom如何通过diff变成真正的dom

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值