使用React或者RN開(kāi)發(fā)APP如果不知道Diff算法的話簡(jiǎn)直是說(shuō)不過(guò)去啊。畢竟“知其然,知其所以然”這句老話從遠(yuǎn)古喊到現(xiàn)代了。
以下內(nèi)容基本是官網(wǎng)文章的一個(gè)總結(jié)残拐、壓縮。這次要謙虛一下碟嘴,畢竟深入研究RN的時(shí)間不多溪食,如果有什么理解的不對(duì)的地方還請(qǐng)各位讀者指正。
用React官網(wǎng)的Thinking in React作為例子娜扇。
這幾個(gè)組件的結(jié)構(gòu)是這樣的:
FilterableProductTable
SearchBar
ProductTable
ProductCategoryRow
ProductRow
相關(guān)代碼错沃,省去不必要的以后是這樣的:
class ProductCategoryRow extends React.Comonent {
render() {
return (<tr><th colSpan="2">{this.props.category}</th></tr>);
}
};
class ProductRow extends React.Component{
render() {
return (
<tr>
<td>{name}</td>
<td>{this.props.product.price}</td>
</tr>
);
}
};
class ProductTable extends React.Component {
render() {
return (
<table>
<tbody>{rows}</tbody>
</table>
);
}
};
class SearchBar extends React.Component{
render() {
return (
<form>
//...略...
</form>
);
}
};
class FilterableProductTable extends React.Component{
render() {
return (
<div>
<SearchBar />
<ProductTable products={this.props.products} />
</div>
);
}
};
ReactDOM.render(
<FilterableProductTable products={PRODUCTS} />,
document.getElementById('container')
);
上面的例子說(shuō)明React的組件最后組成了一個(gè)樹(shù)形結(jié)構(gòu)。在React繪制的時(shí)候雀瓢,會(huì)在內(nèi)存里對(duì)應(yīng)每一個(gè)組件建立一個(gè)節(jié)點(diǎn)枢析,并最終形成一個(gè)和組件樹(shù)結(jié)構(gòu)一樣的樹(shù)。我們就叫這個(gè)樹(shù)叫影子樹(shù)(這個(gè)叫法不是出自官方)刃麸。我們可以理解為這個(gè)影子樹(shù)包含了React App組建的結(jié)構(gòu)和一些屬性值醒叁。
在組件發(fā)生變化的時(shí)候(一般是調(diào)用了setState
),React會(huì)形成一個(gè)影子樹(shù)二號(hào)。然后對(duì)比影子樹(shù)1號(hào)和影子樹(shù)2號(hào)的不同把沼。
我們知道對(duì)比兩個(gè)樹(shù)的最小不同的時(shí)間復(fù)雜度是O(n3
)啊易,n是樹(shù)里的節(jié)點(diǎn)數(shù)。這個(gè)復(fù)雜度下饮睬,稍有量級(jí)的應(yīng)用都會(huì)遇到一個(gè)問(wèn)題:無(wú)法忽略的慢租谈。于是,F(xiàn)B的同學(xué)們使用了更加高效的啟發(fā)式算法捆愁,把復(fù)雜度降低到了O(n)割去。
但是,不管是什么算法最后都需要對(duì)比兩個(gè)節(jié)點(diǎn)的不同昼丑。有三種情況需要考慮:
一劫拗、節(jié)點(diǎn)之間的比較
節(jié)點(diǎn),英語(yǔ)里的Node矾克,包括兩種類型:一個(gè)是React組件,一個(gè)是HTML的DOM憔足。下文也是同樣的含義胁附。
節(jié)點(diǎn)類型不同
如果是HTML DOM不同的話,直接使用新的替換舊的滓彰。
如果是組件類型不同的話也直接使用新的替換舊的控妻。
HTML DOM類型相同
在React里樣式并不是一個(gè)純粹的字符串,而是一個(gè)對(duì)象揭绑,這樣的話在樣式發(fā)生改變的時(shí)候只需要改變替換變化以后的樣式弓候。修改完當(dāng)前節(jié)點(diǎn)之后,遞歸處理該節(jié)點(diǎn)的子節(jié)點(diǎn)他匪。
組件類型相同
組件類型相同的菇存,使用React機(jī)制處理。一般是使用新的props替換掉舊的props邦蜜,并在之后調(diào)用組件的componentWill/DidReceiveProps
方法依鸥,之前的組件的render方法會(huì)被調(diào)用。節(jié)點(diǎn)的比較機(jī)制開(kāi)始遞歸作用于這個(gè)它的子節(jié)點(diǎn)上悼沈。
二贱迟、兩個(gè)列表之間的比較
一列節(jié)點(diǎn)中的一個(gè)發(fā)生了改變,React并沒(méi)有什么好方法來(lái)處理這個(gè)問(wèn)題絮供。循環(huán)新舊兩個(gè)列表衣吠,并找出不同是React唯一的處理方法。
但是壤靶,有一個(gè)可以把這個(gè)算法的復(fù)雜度降低的辦法缚俏。那就是我們?cè)谏梢涣泄?jié)點(diǎn)的時(shí)候給每一個(gè)節(jié)點(diǎn)上添加一個(gè)key。這個(gè)key只需要在這一列節(jié)點(diǎn)中唯一,不需要全局唯一袍榆。
三胀屿、取舍
需要注意的是,上面的啟發(fā)式算法是基于兩點(diǎn)假設(shè):
*類型想聽(tīng)的節(jié)點(diǎn)總是生成同樣的樹(shù)包雀,而類型不同的節(jié)點(diǎn)也總是生成不同的樹(shù)宿崭。
*可以為多次render都表現(xiàn)穩(wěn)定的節(jié)點(diǎn)設(shè)置key。
上面的節(jié)點(diǎn)之間的比較算法基本就是基于這兩個(gè)假設(shè)而實(shí)現(xiàn)的才写。也就是要提高React應(yīng)用的效率葡兑,需要我們按照這兩點(diǎn)假設(shè)來(lái)開(kāi)發(fā)。
否則赞草,React會(huì)重獲整個(gè)APP讹堤。那就是噩夢(mèng)一樣的情況了。