React 組件渲染和更新的實(shí)現(xiàn)代碼示例
最近一直寫React,慢慢就對(duì)里面的一些實(shí)現(xiàn)很好奇。最好奇的就是自定義標(biāo)簽的實(shí)現(xiàn)和this.setState的實(shí)現(xiàn)。這里不分析JSX是如何解析的,所有組件都用ES5方式編寫。
組件渲染
渲染時(shí)候,我們會(huì)調(diào)用render方法。類似下面這樣:
var SayHi = React.createClass({ getInitialState: function() { return {verb: 'say:'}; }, componentWillMount: function() { console.log('I will mount'); }, componentDidMount: function() { console.log('I have mounted'); }, render: function() { return React.createElement("div", null,this.state.verb, "Hello ", this.props.name); } }); React.render(React.createElement(SayHi, {name: "Cynthia"}), document.getElementById("container"));
結(jié)果:
頁面打?。?
say: Hello Cynthia
控制臺(tái)打?。?
I will mount
I have mounted
這是我畫的React對(duì)象上的一些屬性和方法。
當(dāng)調(diào)用render方法時(shí),render會(huì)去調(diào)用一個(gè)map方法,根據(jù)傳入?yún)?shù)的不同,把被render的對(duì)象分為以下三類:
* 文本
* 原生
* 自定義標(biāo)簽
文本
對(duì)于文本,React會(huì)實(shí)例化一個(gè)文本節(jié)點(diǎn)的對(duì)象,并且調(diào)用該對(duì)象的mount方法。在這個(gè)mount方法中,把文本放到一個(gè)span
中,調(diào)用容器組件的innerHTML
,進(jìn)行渲染。
原生標(biāo)簽
對(duì)于原生標(biāo)簽,React會(huì)實(shí)例化一個(gè)處理原生標(biāo)簽的對(duì)象,并且調(diào)用該對(duì)象的mount方法。在這個(gè)mount方法中,拼接一個(gè)字符串,并且不斷遞歸上面的map方法,最后把拼接好的字符串放到容器組件的innerHTML
中,進(jìn)行渲染。
自定義標(biāo)簽
這個(gè)應(yīng)該是大家最好奇的。自定義標(biāo)簽雖然叫標(biāo)簽,其實(shí)就是一個(gè)類。實(shí)例化一個(gè)處理自定義標(biāo)簽的對(duì)象后,首先React會(huì)處理自定義標(biāo)簽的生命周期方法,然后再次遞歸調(diào)用子組件的render方法進(jìn)而調(diào)用map方法,直至把自定義標(biāo)簽分解為前兩種標(biāo)簽。
更新
首先,我們統(tǒng)一一下認(rèn)識(shí)。在React里調(diào)用this.setState()
會(huì)使得組件更新,調(diào)用this.state = {}
只會(huì)更改本組件的狀態(tài),但是不會(huì)使得組件更新。
如果我要更新一個(gè)組件,我會(huì)這樣寫。
var SayHi = React.createClass({ getInitialState: function() { return {verb: 'say:'}; }, componentWillMount: function() { console.log('I will mount'); }, componentDidMount: function() { console.log('I have mounted'); }, changeVerb: function(){ this.setState({verb: 'write:'}); } render: function() { return React.createElement("div", this.changeVerb.bind(this),this.state.verb, "Hello ", this.props.name); } }); React.render(React.createElement(SayHi, {name: "Cynthia"}), document.getElementById("container"));
執(zhí)行結(jié)果:
頁面打?。?
say: Hello Cynthia
點(diǎn)擊文本,頁面內(nèi)容更新成:
write: Hello Cynthia
與更新相關(guān)的屬性和方法如下:
在調(diào)用this.setState()
以后,也是調(diào)用了一個(gè)map方法,根據(jù)傳入?yún)?shù)不同,依然把要更新的標(biāo)簽分為文本、原生標(biāo)簽、自定義標(biāo)簽三類。具體處理過程如下。
文本
文本節(jié)點(diǎn)處理很簡(jiǎn)單,判斷要更新后的文本與當(dāng)前文本是否===
,不是全等就刪除原來文本,插入新文本。
自定義標(biāo)簽
對(duì)于自定義標(biāo)簽,首先根據(jù)對(duì)象的引用、key是否相同,判斷是否需要更新。如果需要更新,就繼續(xù)調(diào)用上述map方法進(jìn)行子組件的更新。又是一個(gè)遞歸。但是注意,這里的map方法和渲染部分的map方法不是一個(gè)方法喲。
原生標(biāo)簽
對(duì)于原生標(biāo)簽,首先更新組件的屬性,然后update子樹,用diff算法來比較新的子樹與目前標(biāo)簽的子樹的不同,形成一個(gè)差異樹,然后用patch方法,把這個(gè)差異樹更新到真正的DOM樹上。
總結(jié)
很復(fù)雜的過程,讓我用流水賬寫了一遍。沒能道出其中精華。以后繼續(xù)探索,寫的詳細(xì)一些。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
React函數(shù)組件與類組件使用及優(yōu)劣對(duì)比
本文主要介紹了React函數(shù)組件與類組件使用及優(yōu)劣對(duì)比,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04為react組件庫(kù)添加typescript類型提示的方法
這篇文章主要介紹了為react組件庫(kù)添加typescript類型提示,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06React中事件綁定this指向三種方法的實(shí)現(xiàn)
這篇文章主要介紹了React中事件綁定this指向三種方法的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05如何創(chuàng)建自己的第一個(gè)React 頁面
React是用于構(gòu)建用戶界面的JavaScript庫(kù),本文主要介紹了如何創(chuàng)建自己的第一個(gè)React頁面,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11使用React?Redux實(shí)現(xiàn)React組件之間的數(shù)據(jù)共享
在復(fù)雜的React應(yīng)用中,組件之間的數(shù)據(jù)共享是必不可少的,為了解決這個(gè)問題,可以使用React?Redux來管理應(yīng)用的狀態(tài),并實(shí)現(xiàn)組件之間的數(shù)據(jù)共享,在本文中,我們將介紹如何使用React?Redux實(shí)現(xiàn)Count和Person組件之間的數(shù)據(jù)共享,需要的朋友可以參考下2024-03-03