摘要:?jiǎn)l(fā)式算法了解一下什么是啟發(fā)式算法啟發(fā)式算法指人在解決問題時(shí)所采取的一種根據(jù)經(jīng)驗(yàn)規(guī)則進(jìn)行發(fā)現(xiàn)的方法。這將會(huì)造成極大的性能損失和組件內(nèi)的丟失。但這都是的內(nèi)部實(shí)現(xiàn)方式,可能在后序的版本中不斷細(xì)化啟發(fā)式算法,甚至采用別的啟發(fā)式算法。
首先歡迎大家關(guān)注我的掘金賬號(hào)和Github博客,也算是對(duì)我的一點(diǎn)鼓勵(lì),畢竟寫東西沒法獲得變現(xiàn),能堅(jiān)持下去也是靠的是自己的熱情和大家的鼓勵(lì)。
大家在使用React的過程中,當(dāng)組件的子元素是一系列類型相同元素時(shí),就必須添加一個(gè)屬性key,否則React將給出一個(gè)warning:
所以我們需要了解一下key值在React中起到了什么作用,在這之前我們先出一個(gè)小題目:
import React from "react" import ReactDOM from "react-dom" function App() { return (
現(xiàn)在要提問了,上面的例子顯示的是: 1,1,2,2還是1,2呢。事實(shí)上顯示的只有1和2,所以我們不禁要問為什么?
我們知道每當(dāng)組件的props和state發(fā)送改變時(shí),React都會(huì)調(diào)用render去重新渲染UI,實(shí)質(zhì)上render函數(shù)作用就是返回最新的元素樹。這里我們要明確一個(gè)點(diǎn): 什么是組件?什么是元素?
React元素是用來描述UI對(duì)象的,JSX的實(shí)質(zhì)就是React.createElement的語法糖,作用就是生成React元素。而React組件是一個(gè)方法或者類(Class),其目的就是接受輸入并返回一個(gè)ReactElement,當(dāng)然調(diào)用React組件一般采用的也是通過JSX的方法,其本質(zhì)也是通過React.createElement方式去調(diào)用組件的。
我們之前說過,組件state和props的改變會(huì)引起render函數(shù)的調(diào)用,而render函數(shù)會(huì)返回新的元素樹。我們知道React使得我們并不需要關(guān)心更改的內(nèi)容,只需要將精力集中于數(shù)據(jù)的變化,React會(huì)負(fù)責(zé)前后UI更新。這時(shí)候React就面臨一個(gè)問題,如果對(duì)比當(dāng)前的元素樹與之前的元素樹,從而找到最優(yōu)的方法(或者說是步驟最少的方法)將一顆樹轉(zhuǎn)化成另一棵樹,從而去更新真實(shí)的DOM元素。目前存在大量的方法可以將一棵樹轉(zhuǎn)化成另一棵樹,但它們的時(shí)間復(fù)雜度基本都是O(n3),這么龐大的時(shí)間數(shù)量級(jí)我們是不能接受的,試想如果我們的組件返回的元素樹中含有100個(gè)元素,那么一次一致性比較就要達(dá)到1000000的數(shù)量級(jí),這顯然是低效的,不可接受的。這時(shí)React就采用了啟發(fā)式的算法。
了解一下什么是啟發(fā)式算法:
啟發(fā)式算法指人在解決問題時(shí)所采取的一種根據(jù)經(jīng)驗(yàn)規(guī)則進(jìn)行發(fā)現(xiàn)的方法。其特點(diǎn)是在解決問題時(shí),利用過去的經(jīng)驗(yàn),選擇已經(jīng)行之有效的方法,而不是系統(tǒng)地、以確定的步驟去尋求答案。
React啟發(fā)式算法就是采用一系列前提和假設(shè),使得比較前后元素樹的時(shí)間復(fù)雜度由O(n3)降低為O(n),React啟發(fā)式算法的前提條件主要包括兩點(diǎn):
不同的兩個(gè)元素會(huì)產(chǎn)生不同的樹
可以使用key屬性來表明不同的渲染中哪些元素是相同的
元素類型的比較 函數(shù)React.createElement的第一個(gè)參數(shù)就是type,表示的就是元素的類型。React比較兩棵元素樹的過程是同步的,當(dāng)React比較到元素樹中同一位置的元素節(jié)點(diǎn)時(shí),如果前后元素的類型不同時(shí),不論該元素是組件類型還是DOM類型的,那么以這個(gè)節(jié)點(diǎn)(React元素)為子樹的所有節(jié)點(diǎn)都會(huì)被銷毀并重新構(gòu)建。舉個(gè)例子:
//old tree//new tree
上面表示前后兩個(gè)render函數(shù)返回的元素樹,由于Counter元素的父元素由div變成了span,那么那就導(dǎo)致Counter的卸載(unmount)和重新安裝(mount)。這看起來沒有什么問題,但是在某些情況下問題就會(huì)凸顯出來,比如狀態(tài)的丟失。下面我們?cè)倏匆粋€(gè)例子:
import React, {Component} from "react" import ReactDOM from "react-dom" class Counter extends Component { constructor(props){ super(props); } state = { value: 0 } componentWillMount(){ console.log("componentWillMount"); } componentDidMount(){ this.timer = setInterval(()=>{ this.setState({ value: this.state.value + 1 }) },1000) } componentWillUnmount(){ clearInterval(this.timer); console.log("componentWillUnmount"); } render(){ return({this.state.value}) } } function Demo(props) { return props.flag ? () : (); } class App extends Component{ constructor(props){ super(props); } state = { flag: false } render(){ return( ) } } ReactDOM.render(, document.getElementById("root"))
上面的例子中,我們首先讓計(jì)數(shù)器Counter運(yùn)行幾秒鐘,然后我們點(diǎn)擊按鈕的話,我們會(huì)發(fā)現(xiàn)計(jì)數(shù)器的值會(huì)歸零為0,并且Counter分別調(diào)用componentWillUnmount與componentWillMount并完成組件卸載與安裝的過程。需要注意的是,狀態(tài)(state)的丟失有時(shí)候會(huì)造成不可預(yù)知的問題,需要尤為注意。
那如果比較前后元素類型是相同的情況下,情況就有所區(qū)別,如果該元素類型是DOM類型,比如:
那么React包保持底層DOM元素不變,僅更新改變的DOM元素屬性,比如在上面的例子中,React僅會(huì)更新div標(biāo)簽的className屬性。如果改變的是style屬性中的某一個(gè)屬性,也不會(huì)整個(gè)更改style,而僅僅是更新其中改變的項(xiàng)目。
如果前后的比較元素是組件類型,那么也會(huì)保持組件實(shí)例的不變,React會(huì)更新組件實(shí)例的屬性來匹配新的元素,并在元素實(shí)例上調(diào)用componentWillReceiveProps() 與 componentWillUpdate()。
在上面的前后元素樹比較過程中,如果某個(gè)元素的子元素是動(dòng)態(tài)數(shù)組類型的,那么比較的過程可能就要有所區(qū)分,比如:
//注意: //li元素是數(shù)組生成的,下面只是表示元素樹,并不代表實(shí)際代碼 //old tree
當(dāng)React同時(shí)迭代比較前后兩棵元素樹的子元素列表時(shí),性能相對(duì)不會(huì)太差,因?yàn)榍皟蓚€(gè)項(xiàng)都是相同的,新的元素樹中有第三個(gè)項(xiàng)目,那么React會(huì)比較
//注意: //li元素是數(shù)組生成的,下面只是表示元素樹,并不代表實(shí)際代碼 //old tree
React在比較第一個(gè)li就發(fā)現(xiàn)了差異(
例如:
//注意: //li元素是數(shù)組生成的,下面只是表示元素樹,并不代表實(shí)際代碼 //old tree
通過key值React比較
回到剛開始的問題,如果存在兩個(gè)key值相同時(shí),會(huì)發(fā)生什么?比如:
我們會(huì)發(fā)現(xiàn)如果存在前后兩個(gè)相同的key,React會(huì)認(rèn)為這兩個(gè)元素其實(shí)是一個(gè)元素,后一個(gè)具有相同key值的元素會(huì)被忽略。為了驗(yàn)證這個(gè)事實(shí),我們可以看下一個(gè)例子:
import React, {Component} from "react" import ReactDOM from "react-dom" function Demo(props) { return ({props.value}) } class App extends Component { constructor(props) { super(props); } render() { return ({ [1, 1, 2, 2].map((val, index) => { return () } } ReactDOM.render() }) } , document.getElementById("root"))
我們發(fā)現(xiàn)最后的顯示效果是這樣的:
到這里我們已經(jīng)基本明白了key屬性在React中的作用,因?yàn)?b>key是React內(nèi)部使用的屬性,所以在組件內(nèi)部是無法獲取到key值的,如果你真的需要這個(gè)值,就需要換個(gè)名字再傳一次了。
其實(shí)還有一個(gè)現(xiàn)象不知道大家觀察到了沒有,比如:
//case1 function App() { return (
我們會(huì)發(fā)現(xiàn),第一種場(chǎng)景是需要傳入key值的,第二種就不需要傳入key,為什么呢?其實(shí)我們可以看一下JSX編譯之后的代碼:
//case1 function App() { return React.createElement("ul",null,[ React.createElement("li",{key: 1}, "1"), React.createElement("li",{key: 2}, "2") ]) } //case2 function App() { return React.createElement("ul", null, React.createElement("li",{key: 1}, "1"), React.createElement("li",{key: 2}, "2") ) }
我們發(fā)現(xiàn)第一個(gè)場(chǎng)景中,子元素的傳入以數(shù)組的形式傳入第三個(gè)參數(shù),但是在第二個(gè)場(chǎng)景中,子元素是以參數(shù)的形式依次傳入的。在第二種場(chǎng)景中,每個(gè)元素出現(xiàn)在固定的參數(shù)位置上,React就是通過這個(gè)位置作為天然的key值去判別的,所以你就不用傳入key值的,但是第一種場(chǎng)景下,以數(shù)組的類型將全部子元素傳入,React就不能通過參數(shù)位置的方法去判別,所以就必須你手動(dòng)地方式去傳入key值。
React通過采用這種啟發(fā)式的算法,來優(yōu)化一致性的操作。但這都是React的內(nèi)部實(shí)現(xiàn)方式,可能在React后序的版本中不斷細(xì)化啟發(fā)式算法,甚至采用別的啟發(fā)式算法。但是如果我們有時(shí)候能夠了解到內(nèi)部算法的實(shí)現(xiàn)細(xì)節(jié)的話,對(duì)于優(yōu)化應(yīng)用性能可以起到非常好的效果,對(duì)于共同學(xué)習(xí)的大家,以此共勉。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/85210.html
摘要:前端日?qǐng)?bào)精選組件庫(kù)設(shè)計(jì)實(shí)戰(zhàn)復(fù)雜組件設(shè)計(jì)高級(jí)技巧源碼分析你不知道的從源碼角度再看數(shù)據(jù)綁定中文整理布局方案?jìng)€(gè)人文章筆記快速入門筆記個(gè)人文章第期重新認(rèn)識(shí)的作用域閉包對(duì)象技術(shù)內(nèi)幕帶來了什么掘金周刊實(shí)戰(zhàn)桌面計(jì)算器應(yīng)用掘金技術(shù)周刊期 2017-09-04 前端日?qǐng)?bào) 精選 組件庫(kù)設(shè)計(jì)實(shí)戰(zhàn) - 復(fù)雜組件設(shè)計(jì)JS高級(jí)技巧Vuex 源碼分析你不知道的CSS從Vue.js源碼角度再看數(shù)據(jù)綁定How to c...
摘要:本系列文章將重點(diǎn)分析類似于的這類框架是如何實(shí)現(xiàn)的,歡迎大家關(guān)注和討論。作為一個(gè)極度精簡(jiǎn)的庫(kù),函數(shù)是屬于本身的。 前言 首先歡迎大家關(guān)注我的掘金賬號(hào)和Github博客,也算是對(duì)我的一點(diǎn)鼓勵(lì),畢竟寫東西沒法獲得變現(xiàn),能堅(jiān)持下去也是靠的是自己的熱情和大家的鼓勵(lì)?! ≈胺窒磉^幾篇關(guān)于React的文章: React技術(shù)內(nèi)幕: key帶來了什么 React技術(shù)內(nèi)幕: setState的秘密...
摘要:微信公眾號(hào)愛寫的阿拉斯加如有問題或建議,請(qǐng)后臺(tái)留言,我會(huì)盡力解決你的問題。而技術(shù)內(nèi)幕是基于的項(xiàng)目的講解。有興趣的朋友可以掃下方二維碼公眾號(hào)愛寫的阿拉斯加分享開發(fā)相關(guān)的技術(shù)文章,熱點(diǎn)資源,全棧程序員的成長(zhǎng)之路和大家一起交流成長(zhǎng)。 微信公眾號(hào):愛寫bugger的阿拉斯加如有問題或建議,請(qǐng)后臺(tái)留言,我會(huì)盡力解決你的問題。 前言 此文章是我最近在看的【W(wǎng)ebKit 技術(shù)內(nèi)幕】一書的一些理解和做...
閱讀 4392·2021-11-24 10:24
閱讀 1419·2021-11-22 15:22
閱讀 2048·2021-11-17 09:33
閱讀 2457·2021-09-22 15:29
閱讀 526·2019-08-30 15:55
閱讀 1666·2019-08-29 18:42
閱讀 2742·2019-08-29 12:55
閱讀 1784·2019-08-26 13:55