淺談React底層實現(xiàn)原理
1. props,state與render函數(shù)關(guān)系,數(shù)據(jù)和頁面如何實現(xiàn)互相聯(lián)動?
當(dāng)組件的state或者props發(fā)生改變的時候,自己的render函數(shù)就會重新執(zhí)行。
注意:當(dāng)父組件的render被執(zhí)行的時候,子組件的render也會被重新執(zhí)行一次(因為在父組件的render里面)。
即當(dāng)綁定的事件改變了state或者props,render函數(shù)就會重新執(zhí)行解析頁面,這時解析的時候就會使用新的數(shù)據(jù)了,所以頁面就會變化。
2. React中的虛擬DOM
因為只要state、props改變就會重新渲染render,可以想象要不斷的重新渲染頁面對性能要求非常高,實際上render的性能時非常高的,這歸功于虛擬DOM。
首先提前明確DOM的相關(guān)操作需要調(diào)用web application對性能損耗是比較高的。
常規(guī)思路
- state數(shù)據(jù)
- JSX模板
- 數(shù)據(jù)+模板相結(jié)合,生成真實的DOM來展示。
- state改變
- 數(shù)據(jù)+模板相結(jié)合,生成真實的DOM,替換原始的DOM
缺點:第一次生成了一個完整的DOM片段,第二次又生成了一個完整的DOM片段,第二次的DOM替換第一次的DOM,這樣生成、替換非常的消耗性能。
改良思路(仍使用DOM)
- state數(shù)據(jù)
- JSX模板
- 數(shù)據(jù)+模板相結(jié)合,生成真實的DOM來展示
- state改變
- 數(shù)據(jù)+模板相結(jié)合,生成真實的DOM,并不直接替換原始的DOM
- 新的DOM(文檔碎片)原始的DOM作對比,找差異(性能損耗大)
- 找出發(fā)生了什么變化,比如找出了只有input框有差異
- 只用新的DOM中的input元素替換掉老的DOM中的input元素
缺點:性能提升并不明顯,因為性能消耗在了對比上。
React的思路
- state數(shù)據(jù)
- JSX模板
- 數(shù)據(jù)+模板相結(jié)合,生成虛擬DOM(虛擬DOM就是一個JS數(shù)組對象,完整的描述真實的DOM)( [ ‘ idv ‘ , { id : ‘ abc ‘ } , [ ‘ span ‘ , { } , ‘ hello ‘ ] ] ),用JS生成JS對象性能損耗極小,生成DOM性能損耗大要調(diào)用web application。
- 用虛擬DOM的結(jié)構(gòu)生成真實的DOM,來顯示( < div id=’abc’>< span>hello</ span></ div> )。
- state發(fā)生變化(< div id=’abc’>< span>bye</ span></ div>)
- 數(shù)組+模板生成了新的虛擬DOM( [ ‘ idv ‘ , { id : ‘ abc ‘ } , [ ‘ span ‘ , { } , ‘ bye ‘ ] ] )(極大的提升了性能)
- 比較原始虛擬DOM和新的虛擬DOM的區(qū)別,找到區(qū)別是span中的內(nèi)容(極大的提升了性能)
- 直接操作DOM改變span中的內(nèi)容
總結(jié):減少了對真實DOM的創(chuàng)建和對比,而創(chuàng)建和對比的是JS對象,從而實現(xiàn)了極大的性能飛躍。
深入理解虛擬DOM
Vue與React中的虛擬DOM的原理和步驟是完全一致的。
React中真實DOM的生成步驟:JSX -> createElement方法 -> JS對象(虛擬DOM) -> 真實的DOM。
因此可見,JSX中的div等標(biāo)簽僅僅是JSX的語法,并不是DOM,僅用于生成JS對象。
其實在React中創(chuàng)建虛擬DOM(JS對象)使用的是(沒有JSX語法也可用下面的方法生成)
// 傳三個參數(shù):標(biāo)簽 屬性 內(nèi)容 // <div>item</div> // 所以其實沒有JSX語法也可以用下面的方式生成 React.createElement('div',{},'item')
虛擬DOM的優(yōu)點:
- 性能提升DOM對比變成了JS的對比
- 它使得跨平臺應(yīng)用得以實現(xiàn),React Native(安卓和ios中沒有DOM的概念,使用虛擬DOM(JS對象)在所有應(yīng)用中都可以被使用,然后變成原生客戶端的組件)
3. 虛擬DOM的diff算法
- Diff算法用于比較原始虛擬DOM和新的虛擬DOM的區(qū)別,即兩個JS對象該如何比對。
- diff算法全稱為difference算法
- setState實際上是異步的,這是為了提升react底層的性能,是為了防止時間間隔很短的情況下多次改變state,React會在這種情況下將幾次改變state合并成一次從而提高性能。
- diff算法是同級比較,假設(shè)第一層兩個虛擬DOM節(jié)點不一致,就不會往下比了,就會將原始頁面虛擬DOM全部刪除掉,然后用新的虛擬DOM進(jìn)行全部的替換,雖然有可能有一些性能的浪費,但是由于同層對比的算法性能很高,因此又彌補了性能的損耗。
- 做list循環(huán)的時候有一個key值,這樣比對的時候就可以相對應(yīng)的比對,找出要改變的,以及不需要渲染的,這樣使用key做關(guān)聯(lián),極大地提升了虛擬DOM比對的性能,這要保證在新的虛擬DOM后key值不變,這就說明了為什么做list循環(huán)的時候key的值不要是index,因為這樣沒有辦法保證原虛擬DOM和新虛擬DOM的key值一致性,而造成性能的損耗,一般這個key對應(yīng)后臺數(shù)據(jù)的唯一id字段,而不是循環(huán)的index。
4. React中ref的使用
- 在react中使用ref來操作DOM
- 在react中也可以使用e.target來獲取DOM
- ref這個參數(shù)是一個函數(shù)
<input ? ? id = "insertArea" ? ? className="input" ? ? value={this.state.inputValue} ? ? onChange={this.handleInputChange} ? ? ref={(input)=>{this.input = input}} /> handleInputChange(e){ ?? ?// const value = e.target.value; // 原始的方法 ?? ?const value = this.input.value; ?? ?this.setState(() => ({ ?? ??? ?inputValue: value ?? ?})) }
一般情況下不推薦使用ref這種方法,因為setState是一個異步函數(shù),因此去操作DOM的時候可能無法正確的輸出頁面的最新DOM情況,有時候比較復(fù)雜的操作如動畫之類的,如果一定要使用,就需要在setState的第二個函數(shù),這個是回調(diào)函數(shù),在setState完成的時候觸發(fā)。
handleBtnClick(e){ this.setState((prevState)=>({ list: [...prevState.list, prevState.inputValue], // 展開運算符 inputValue: '', }), ()=>{ console.log(this.ul.querySelectorAll('div').length); }); }
5. React中的生命周期函數(shù)
- 生命周期函數(shù)是指在某一個時刻組件會自動調(diào)用執(zhí)行的函數(shù)。
- render函數(shù)就是一個生命周期函數(shù)的例子,當(dāng)state或props的時候改變的時刻就會自動執(zhí)行。
- contructor 可以理解成一個生命周期函數(shù),在組件被創(chuàng)建的時候就會被執(zhí)行,但是它是es6語法,不是react特殊的語法。
組件掛載的過程:
- componentWillMount 在組件即將被掛載到頁面的時刻自動執(zhí)行,在渲染之前被執(zhí)行
- render 進(jìn)行掛載,是必須存在的
- componentDidMount 在組件被掛載到頁面之后被執(zhí)行。
- 注意:在state和props 改變的時候只有render會執(zhí)行,componentWillMount和componentDidMount不會執(zhí)行,他們只會在第一次掛載到頁面的時候被執(zhí)行。
- 組件更新:
- componentWillReceiveProps 兩個條件都要滿足:1. 當(dāng)一個組件從父組件接收參數(shù) 2. 如果這個組件第一次存在于父組件中不會執(zhí)行,如果這個組件之前已經(jīng)存在于父組件中,才會執(zhí)行。
- shouldComponentUpdate 組件即將被更新之前會執(zhí)行,如焦點input框的時候,會返回一個true和false來判斷要不要更新。
- componentWillUpdate 組件更新之前會自動執(zhí)行,在shouldComponent返回true之后才會執(zhí)行。
- componentDidUpdate 組件更新完成之后被執(zhí)行。
- 組件去除的過程:
- componentWillUnmount:但這個組件即將被從頁面中剔除的時候執(zhí)行。
6. 生命周期函數(shù)的使用場景
- 防止父組件render的時候,子組件也要render,從而提升性能。
- shouldComponentUpdate(nextProps,nextState){if(nextProps.content !== this.props.content){return true} return false}
- 頁面初始化的時候,在componentDidMount中發(fā)送AJAX請求(推薦),或者在constructor中,千萬不要放在render里面,會造成死循環(huán),也最好不要在componentWillMount中發(fā)送ajax,放這里面是沒有問題的,但是如果在react native 中會有問題。
- react 沒有內(nèi)置ajax,使用axios。
到此這篇關(guān)于淺談React底層實現(xiàn)原理的文章就介紹到這了,更多相關(guān)React底層內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
D3.js(v3)+react 實現(xiàn)帶坐標(biāo)與比例尺的散點圖 (V3版本)
散點圖(Scatter Chart),通常是一橫一豎兩個坐標(biāo)軸,數(shù)據(jù)是一組二維坐標(biāo),分別對應(yīng)兩個坐標(biāo)軸,與坐標(biāo)軸對應(yīng)的地方打上點。由此可以猜到,需要的元素包括circle(圓)和axis(坐標(biāo)軸),接下來通過本文大家分享D3.js(v3)+react 實現(xiàn)帶坐標(biāo)與比例尺的散點圖 (V3版本) ,一起看看2019-05-05React在弱網(wǎng)環(huán)境下限制按鈕多次點擊,防止重復(fù)提交問題
這篇文章主要介紹了React在弱網(wǎng)環(huán)境下限制按鈕多次點擊,防止重復(fù)提交問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06React-redux實現(xiàn)小案例(todolist)的過程
這篇文章主要為大家詳細(xì)介紹了React-redux實現(xiàn)小案例(todolist)的過程,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-09-09通過React-Native實現(xiàn)自定義橫向滑動進(jìn)度條的 ScrollView組件
開發(fā)一個首頁擺放菜單入口的ScrollView可滑動組件,允許自定義橫向滑動進(jìn)度條,且內(nèi)部渲染的菜單內(nèi)容支持自定義展示的行數(shù)和列數(shù),在內(nèi)容超出屏幕后,渲染順序為縱向由上至下依次排列,對React Native橫向滑動進(jìn)度條相關(guān)知識感興趣的朋友一起看看吧2024-02-02