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不存在的元素