示例
- oldArray: ['a','b','c','d','e']
- newArray: ['a','b','f','c','d','e']
示意图
-
有key时
-
无key时
diff过程
-
有key时
// 1. patch a --- 相同节点,复用
a b c d e
a b f c d e
// 2. patch b --- 相同节点,复用
b c d e
b f c d e
// 3. patch e --- 相同节点,复用
c d e
f c d e
// 4. patch d --- 相同节点,复用
c d
f c d
// 5. patch c --- 相同节点,复用
c
f c
// 6. newArray 中剩下f,创建f并添加到c前面
// 实际上只做了一次创建及追加操作
-
无key时
// 1. patch a --- 相同节点,复用
a b c d e
a b f c d e
// 2. patch b --- 相同节点,复用
b c d e
b f c d e
// 3. patch c --- 更新
c d e
f c d e
// 4. patch d --- 更新
d e
c d e
// 5. patch e --- 更新
e
d e
// newArray 中剩下f,创建f并添加到c前面
// 实际上做了三个更新,一个创建及追加操作
源码
src\core\vdom\patch.js updateChildren()
// updateChilren 中的 sameVnode()
function sameVnode (a, b) {
return (
// 没有设置key值时,key = undefined, 恒相等, 使用中后面的属性大概率不会变
a.key === b.key &&
a.asyncFactory === b.asyncFactory && (
(
a.tag === b.tag &&
a.isComment === b.isComment &&
isDef(a.data) === isDef(b.data) &&
sameInputType(a, b)
) || (
isTrue(a.isAsyncPlaceholder) &&
isUndef(b.asyncFactory.error)
)
)
)
}
结论
- key的作用主要是为了高效更新VDOM,其原理是在patch过程中判断是否为相同节点,从而避免频繁更新不同的元素,减少DOM操作,提高性能。
- 若不设置key,可能在列表更新时引发隐蔽的bug。
- vue中使用相同标签名元素的过渡切换时,key属性是为了让vue可以区分他们,否则只会替换内部属性,不会触发过渡效果。