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