vue 用key拿对象value_(Vue) key值的作用

本文探讨了Vue中key值的作用,通过实践展示了key值改变导致组件重新挂载,触发完整生命周期。分析Vue的diff算法,指出key值在决定节点是否相同中起到关键作用,避免了不必要的DOM操作。在列表渲染时,使用唯一的id作为key值能确保正确跟踪每个项,避免因使用index导致的就地复用问题。
摘要由CSDN通过智能技术生成

今天思考一个问题,在子组件中,key值的作用是什么?

如果一个组件,改边key的值,,发生什么?

实践出真理,测试一下:

首先,创建一个子组件:

//components/keyCom.vue

{{ptext}}

一个非常简单的组件,在各个生命周期上,绑定事件:

export default {

data(){

return {

ptext:"测试文本"

}

},

beforeCreate(){

console.log('enter beforeCreate')

},

created(){

console.log('enter created')

},

beforeMount(){

console.log('enter beforeMount')

},

mounted(){

console.log('enter mounted')

},

beforeDestroy(){

console.log('enter beforeDestroy')

},

destroyed(){

console.log('enter destroyed')

}

}

接下来,通过父组件修改子组件的key值:

import KeyCom from '@/components/keyCom'

export default {

data(){

return {

key:1

}

},

components:{

KeyCom

},

methods:{

changeKey:function(){

this.key=2;

}

}

}

看看运行起来是什么情况:

切换key值之前

点击按钮之后,发现:

修改了key值之后

组件经历了一个全新的生命周期,这是为何?为什么同样一个组件,仅仅改变了它上面的key值,就会重新挂载一个新组件?

之前了解到,key值的最大作用,是在渲染列表的时候,diff算法使用到,那么我们就来看看diff的过程是如何?

分析vue的源码,可以知道,diff算法是从patch函数开始:

patch:function(oldVnode,vnode){

if(sameVnode(oldVnode,vnode)){

patchVnode(oldVnode,vnode)

} else {

const oEl = oldVnode.el;

let parentEle = api.parentNode(oEl);

createEle(vnode);

if(parent!==null){

api.insertBefore(parentEle,vnode.el,api.nextSibling(oEl));

api.removeChild(parentEle,oldVnode.el);

oldVnode = null

}

}

return vnode;

},

通过patch函数,可以看到,首先需要对比两个节点是否是相同节点,(相同的组件,难道不是相同节点吗?)

进入sameVnode函数看看:

sameVnode(a,b){

return (

a.key === b.key && // key值

a.tag === b.tag && // 标签名

a.isComment === b.isComment && //是否为注释节点

//是否都定义了data,data包含一些具体信息,例如onclick

isDef(a.data) === isDef(b,data) &&

sameInputType(a,b) //当标签是input,type必须相同

)

}

恍然大悟,原来在diff的时候,不仅是对比元素的标签名,还会去对比元素的key值,key值一旦改变,就算子节点的内容一模一样,也是会进入到patch函数的else中,那么这个时候,执行的操作就是新建新组件=>删除旧组件=>添加新组件。

因此,可以看到生命周期是新组件的生命周期先执行,再进行旧组件的销毁,接着挂载新组件。

emmmm...

那么再思考深一层的问题,如果是列表渲染的时候,key值设为id,和index会有什么区别呢??

同样的做一个测验:创建一个子组件,子组件里包含一个孙子组件

{{text}}

delete

export default {

name: "Child",

props: ["text"],

data() {

return {

x: "在这输入"

};

},

methods: {

onDelete() {

this.$emit("delete");

}

}

};

接着,在原先的组件里:

创建2个Child组件,它们的区别就是一个使用index作为key,一个使用id作为key:

data(){

return {

ptext:"测试文本",

array: ['111','222','333'],

array2: [{id:1,value:'文本1'},{id:2,value:'文本2'},{id:3,value:'文本3'}]

}

}

运行之后就可以看到它们的区别:

image.png

先看上面的三行,这个是使用index作为key值的组件,当修改其中222这行的input值,然后点击删除:

改变第二行的input值

删除第二项之后

删除之后发现,这与我们的预知不符呀,因为 data 里的数组从 [1,2,3] 变成了 [1,3]。

这个可以看到vue中数组遍历的规则:首先对比1和1,发现1没变,然后对比2和3,发现2变成了3,接着对比3和undefined,把3删掉。

所以步骤是:2变成3=>删除3。

那么在删除的时候,因为input的值是孙子组件,里面的值不受2变成3的影响,所以就地复用。

再看下面这个列表,使用id作为key值。当我们修改了第二项的input值,然后删除第二项的时候,会把第二项完全删掉,符合我们的预期:

修改第二项的input值

删除第二项之后

原本的数组是:

array2: [

{id:1,value:'文本1'},

{id:2,value:'文本2'},

{id:3,value:'文本3'}

]

点击删除之后数组是:

array2: [

{id:1,value:'文本1'},

{id:3,value:'文本3'}

]

先对比id从[1,2,3]变成了[1,3],即第二项被删除了。

因此:key值为何不能用index作为值?

如果你用index作为key值的时候,在删除第二项时,index就从1,2,3变成1,2;而不是1,3。

结论

VUE是通过比对组件自身新旧vdom进行更新的。key的作用是辅助判断新旧vdom节点在逻辑上是不是同一个对象。

因此可以确定,渲染列表时,key值需要一个唯一确定的id来赋值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值