天天看點

React中key屬性的作用及原了解析

Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `Test`. See https://fb.me/react-warning-keys for more information.

相信在react的使用過程中,大家或多或少都會遇到過這樣的警告,這個警告是提醒開發者,需要對渲染的元件添加key屬性,那麼,這個key屬性的作用到底是什麼呢?

我們先來看一看下面的段執行個體代碼和它的運作效果

import React, {Component} from 'react';
class Test extends Component {
    constructor(props) {
        super(props);
        this.state = {
            testArray: [{text: '元件1', id: 'a'}, {text: '元件2', id: 'b'}, {text: '元件3', id: 'c'}, {text: '元件4', id: 'd'}]
        }
    }

    //修改state打亂順序
    sort(){
        this.setState({
            testArray: [{text: '元件1', id: 'a'}, {text: '元件3', id: 'c'}, {text: '元件2', id: 'b'}, {text: '元件4', id: 'd'}]
        })
    }
    
    render() {
        return <div>
            <div>不指定key屬性</div>
            <ul>
                {
                    this.state.testArray.map((item) => {
                        return <li ><span>{item.text}</span><input/></li>
                    })
                }
            </ul>
            <div>指定key屬性</div>
            <ul>
                {
                    this.state.testArray.map((item) => {
                        return <li key={item.id}><span>{item.text}</span><input/></li>
                    })
                }
            </ul>
            <button onClick={::this.sort}>打亂排序</button>
        </div>
    }
}

export default Test
           

代碼很簡單,我們做了一個小試驗,通過同一個數組testArray來渲染兩個不同的清單,一個清單項指定了key屬性,另一個不指定key屬性,然後我們觀察他們打亂前後的運作結果。

打亂順序前,在input中填入内容

React中key屬性的作用及原了解析

打亂順序後

React中key屬性的作用及原了解析

我們可以觀察一下,打亂順序後,有無指定key屬性運作結果的異同。相同的是,每一個項的input中的value都得到了保留,不同的是,如果我們不指定key屬性,清單中元件的标題和input在打亂順序之後,好像已經對不上号了,那麼,是什麼原因造成的呢?

我們來簡單的了解一下react的diff算法政策,我們都知道,react為了提升渲染性能,在内部維持了一個虛拟dom,當渲染結構有所變化的時候,會在虛拟dom中先用diff算法先進行一次對比,将所有的差異化解決之後,再一次性根據虛拟dom的變化,渲染到真實的dom結構中。

而key屬性的使用,則涉及到diff算法中同級節點的對比政策,當我們指定key值時,key值會作為目前元件的id,diff算法會根據這個id來進行比對。如果周遊新的dom結構時,發現元件的id在舊的dom結構中存在,那麼react會認為目前元件隻是位置發生了變化,是以不會将舊的元件銷毀重新建立,隻會改變目前元件的位置,然後再檢查元件的屬性有沒有發生變化,然後選擇保留或修改目前元件的屬性,是以我們可以發現如果我們指定了唯一的key值,如果隻是打亂了資料源,資料源渲染出來的每一個子元件都是整體資料發生變化,而如果不顯式指定key值,結果好像有點出乎我們的意料。

那麼,如果沒有顯式指定key值,會發生什麼事情呢?其實,如果沒有顯式指定,react會把目前元件資料源的index作為預設的key值,那麼,這時候會發生什麼事呢?我們以第二項作為例子,由于我們沒有顯式指定key值,key值會被預設指定為index,也就是1。當我們打亂了資料的順序,資料源的第二項由{text: '元件2', id: 'b'}變成了{text: '元件3', id: 'c'},這時候執行diff算法時,發現key值為1的元件在舊的dom結構中存在,并且元件的位置還是原來的位置,是以,直接保留了原元件,但是元件的标題屬性已經改變了,接着,修改元件的屬性,渲染,于是,我們就看到了,輸入框沒改變,但是标題變了,很顯然,這個結果,有時候并不是我們的本意。而如果我們顯式指定了唯一的key值,依舊以第二項作為例子,執行diff算法時,發現第二項的元件變化了并且新的元件在舊的dom結構中存在,于是将第三項整體移動到第二項,然後檢查屬性有沒有發生變化,渲染,最終出現的結果,就是整體的順序改變了。

是以,在實際開發使用中,我們需要注意什麼呢?

首先,我們要確定key值的唯一,事實上如果key值不唯一的話,react隻會渲染第一個,剩下的react會認為是同一項,直接忽略。其次,盡量避免使用index值作為元件的key值,雖然顯式使用index作為key值可以消除warning,但是,我們舉例出現的情況依舊會出現。