(Vue) key值的作用

今天思考一個(gè)問(wèn)題搓幌,在子組件中杆故,key值的作用是什么?
如果一個(gè)組件溉愁,<A key="1" />改邊key的值处铛,<A key="2" />,發(fā)生什么?
實(shí)踐出真理撤蟆,測(cè)試一下:
首先奕塑,創(chuàng)建一個(gè)子組件:

//components/keyCom.vue
<template>
  <div>
      <p>{{ptext}}</p>
  </div>
</template>

一個(gè)非常簡(jiǎn)單的組件,在各個(gè)生命周期上家肯,綁定事件:

export default {
    data(){
        return {
            ptext:"測(cè)試文本"
        }
    },
    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')
    }
}

接下來(lái)龄砰,通過(guò)父組件修改子組件的key值:

<template>
<div>
  <Key-Com :key="key" />
  <a class='change-btn' @click='changeKey()'>切換key</a>
</div>
</template>

<script>
import KeyCom from '@/components/keyCom'
export default {
    data(){
        return {
            key:1
        }
    },
    components:{
        KeyCom
    },
    methods:{
        changeKey:function(){
            this.key=2;
        }
    }
}
</script>

看看運(yùn)行起來(lái)是什么情況:

切換key值之前

點(diǎn)擊按鈕之后,發(fā)現(xiàn):

修改了key值之后

組件經(jīng)歷了一個(gè)全新的生命周期息楔,這是為何寝贡?為什么同樣一個(gè)組件,僅僅改變了它上面的key值值依,就會(huì)重新掛載一個(gè)新組件?
之前了解到碟案,key值的最大作用愿险,是在渲染列表的時(shí)候,diff算法使用到价说,那么我們就來(lái)看看diff的過(guò)程是如何辆亏?
分析vue的源碼,可以知道鳖目,diff算法是從patch函數(shù)開(kāi)始:

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;
},

通過(guò)patch函數(shù)扮叨,可以看到,首先需要對(duì)比兩個(gè)節(jié)點(diǎn)是否是相同節(jié)點(diǎn)领迈,(相同的組件彻磁,難道不是相同節(jié)點(diǎn)嗎?)
進(jìn)入sameVnode函數(shù)看看:

        sameVnode(a,b){
            return (
                a.key === b.key && // key值
                a.tag === b.tag && // 標(biāo)簽名
                a.isComment === b.isComment && //是否為注釋節(jié)點(diǎn)
                //是否都定義了data,data包含一些具體信息狸捅,例如onclick
                isDef(a.data) === isDef(b,data) &&
                sameInputType(a,b) //當(dāng)標(biāo)簽是input,type必須相同
            )
        }

恍然大悟衷蜓,原來(lái)在diff的時(shí)候,不僅是對(duì)比元素的標(biāo)簽名尘喝,還會(huì)去對(duì)比元素的key值磁浇,key值一旦改變,就算子節(jié)點(diǎn)的內(nèi)容一模一樣朽褪,也是會(huì)進(jìn)入到patch函數(shù)的else中置吓,那么這個(gè)時(shí)候,執(zhí)行的操作就是新建新組件=>刪除舊組件=>添加新組件缔赠。
因此衍锚,可以看到生命周期是新組件的生命周期先執(zhí)行,再進(jìn)行舊組件的銷毀橡淑,接著掛載新組件构拳。

emmmm...
那么再思考深一層的問(wèn)題,如果是列表渲染的時(shí)候,key值設(shè)為id置森,和index會(huì)有什么區(qū)別呢斗埂??
同樣的做一個(gè)測(cè)驗(yàn):創(chuàng)建一個(gè)子組件凫海,子組件里包含一個(gè)孫子組件

<template>
  <div>
    {{text}}
    <input v-model="x">
    <button @click="onDelete">delete</button>
  </div>
</template>

<script>
export default {
  name: "Child",
  props: ["text"],
  data() {
    return {
      x: "在這輸入"
    };
  },
  methods: {
    onDelete() {
      this.$emit("delete");
    }
  }
};
</script>

接著呛凶,在原先的<key-com/>組件里:

<template>
  <div>
      <Child class="child" v-for="(item,i) in array" :key='i' :text="item" @delete="remove(i)" />
      <Child class="child" v-for="(item,i) in array2" :key='item.id' :text="item.value" @delete="remove2(i)" />
  </div>
</template>

創(chuàng)建2個(gè)Child組件,它們的區(qū)別就是一個(gè)使用index作為key行贪,一個(gè)使用id作為key:

data(){
    return {
            ptext:"測(cè)試文本",
            array: ['111','222','333'],
            array2: [{id:1,value:'文本1'},{id:2,value:'文本2'},{id:3,value:'文本3'}]
    }
}

運(yùn)行之后就可以看到它們的區(qū)別:

image.png

先看上面的三行漾稀,這個(gè)是使用index作為key值的組件,當(dāng)修改其中222這行的input值建瘫,然后點(diǎn)擊刪除:
改變第二行的input值

刪除第二項(xiàng)之后

刪除之后發(fā)現(xiàn)崭捍,這與我們的預(yù)知不符呀,因?yàn)?data 里的數(shù)組從 [1,2,3] 變成了 [1,3]啰脚。
這個(gè)可以看到vue中數(shù)組遍歷的規(guī)則:首先對(duì)比1和1殷蛇,發(fā)現(xiàn)1沒(méi)變,然后對(duì)比2和3橄浓,發(fā)現(xiàn)2變成了3粒梦,接著對(duì)比3和undefined,把3刪掉荸实。
所以步驟是:2變成3=>刪除3匀们。
那么在刪除的時(shí)候,因?yàn)閕nput的值是孫子組件准给,里面的值不受2變成3的影響泄朴,所以就地復(fù)用
再看下面這個(gè)列表圆存,使用id作為key值叼旋。當(dāng)我們修改了第二項(xiàng)的input值,然后刪除第二項(xiàng)的時(shí)候沦辙,會(huì)把第二項(xiàng)完全刪掉夫植,符合我們的預(yù)期:
修改第二項(xiàng)的input值

刪除第二項(xiàng)之后

原本的數(shù)組是:

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

點(diǎn)擊刪除之后數(shù)組是:

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

先對(duì)比id從[1,2,3]變成了[1,3],即第二項(xiàng)被刪除了油讯。
因此:key值為何不能用index作為值详民?
如果你用index作為key值的時(shí)候,在刪除第二項(xiàng)時(shí)陌兑,index就從1沈跨,2,3變成1兔综,2饿凛;而不是1狞玛,3。

結(jié)論

VUE是通過(guò)比對(duì)組件自身新舊vdom進(jìn)行更新的涧窒。key的作用是輔助判斷新舊vdom節(jié)點(diǎn)在邏輯上是不是同一個(gè)對(duì)象心肪。
因此可以確定,渲染列表時(shí)纠吴,key值需要一個(gè)唯一確定的id來(lái)賦值硬鞍。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市戴已,隨后出現(xiàn)的幾起案子固该,更是在濱河造成了極大的恐慌,老刑警劉巖糖儡,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伐坏,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡休玩,警方通過(guò)查閱死者的電腦和手機(jī)著淆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)拴疤,“玉大人,你說(shuō)我怎么就攤上這事独泞∧欧” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵懦砂,是天一觀的道長(zhǎng)蜒犯。 經(jīng)常有香客問(wèn)我,道長(zhǎng)荞膘,這世上最難降的妖魔是什么罚随? 我笑而不...
    開(kāi)封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮羽资,結(jié)果婚禮上淘菩,老公的妹妹穿的比我還像新娘。我一直安慰自己屠升,他們只是感情好潮改,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著腹暖,像睡著了一般汇在。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脏答,一...
    開(kāi)封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天糕殉,我揣著相機(jī)與錄音亩鬼,去河邊找鬼。 笑死阿蝶,一個(gè)胖子當(dāng)著我的面吹牛雳锋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赡磅,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼魄缚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了焚廊?” 一聲冷哼從身側(cè)響起冶匹,我...
    開(kāi)封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咆瘟,沒(méi)想到半個(gè)月后嚼隘,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡袒餐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年飞蛹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灸眼。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡卧檐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出焰宣,到底是詐尸還是另有隱情霉囚,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布匕积,位于F島的核電站盈罐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏闪唆。R本人自食惡果不足惜盅粪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悄蕾。 院中可真熱鬧票顾,春花似錦、人聲如沸笼吟。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)贷帮。三九已至戚揭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間撵枢,已是汗流浹背民晒。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工精居, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人潜必。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓靴姿,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親磁滚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子佛吓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 什么是diff算法 react 作為一款最主流的前端框架之一,在設(shè)計(jì)的時(shí)候除了簡(jiǎn)化操作之外垂攘,最注重的地方就是節(jié)省性...
    鶴仔z閱讀 1,249評(píng)論 0 7
  • 1 .index值不是一定不變的维雇,如果不加key值的話,刪除前面的項(xiàng)晒他。后面的index可能變也可能不變吱型,比如加個(gè)定...
    skoll閱讀 18,408評(píng)論 1 8
  • 文章首發(fā)于個(gè)人博客 這是我 Deep In React 系列的第二篇文章,如果還沒(méi)有讀過(guò)的強(qiáng)烈建議你先讀第一篇:詳...
    勿忘巛心安閱讀 1,049評(píng)論 1 2
  • width: 65%;border: 1px solid #ddd;outline: 1300px solid #...
    邵勝奧閱讀 4,820評(píng)論 0 1
  • 目前陨仅,前端領(lǐng)域中 React 勢(shì)頭正盛津滞,使用者眾多卻少有能夠深入剖析內(nèi)部實(shí)現(xiàn)機(jī)制和原理。本系列文章希望通過(guò)剖析 R...
    流動(dòng)碼文閱讀 971評(píng)論 0 11