react element是“React.createElement”函數(shù)的返回值,即ReactElement;ReactElement的結(jié)構(gòu)是“const element = {Element $$typeof: REACT_ELEMENT_TYPE,key: key,ref: ref,props: props,_owner: owner, };”。
本教程操作環(huán)境:Windows10系統(tǒng)、react16.9.0版、Dell G3電腦。
react element什么意思?
React源碼 | ReactElement
說到ReactElement,不得不提到的就是在React中,用來替代JavaScript(JS)的語言,JSX。
JSX
作為React的官方指定語法,JSX允許用戶在JS代碼中插入HTML代碼。但是,這種寫法瀏覽器是無法解析的。他們需要一個轉(zhuǎn)換器,Babel就充當(dāng)了這樣一個角色,他在JSX代碼編譯時候?qū)⑵滢D(zhuǎn)換成JS文件,這樣瀏覽器就能解析了。
怎么轉(zhuǎn)換呢,我們知道,JSX有JS和HTMl兩種寫法,本身就是JS寫法的其實是不需要轉(zhuǎn)換的,當(dāng)然也不能說的這么絕對,有時候Babel會為了兼容性的緣故將高版本的語法翻譯到低版本,這部分不在討論范圍。我們要關(guān)注的其實是HTMl的處理方式。
比如下面這行代碼:
<div id='name'>Tom and Jerry</div>
登錄后復(fù)制
通過Babel轉(zhuǎn)換后生成的代碼是:
React.createElement("div", { id: "name"}, "Tom and Jerry");
登錄后復(fù)制
HTML語法轉(zhuǎn)變成了JS語法,簡單來說,我們所寫的JSX最終變成了JS。
我們寫一個復(fù)雜點的例子:
<div class='wrapper' id='id_wrapper'> <span>Tom</span> <span>Jerry</span></div>React.createElement("div", { class: "wrapper", id: "id_wrapper" }, React.createElement("span", null, "Tom"), React.createElement("span", null, "Jerry"));
登錄后復(fù)制
轉(zhuǎn)換規(guī)則是比較簡單的,React.createElement的第一個參數(shù)是節(jié)點類型;第二個參數(shù)是該節(jié)點的屬性,以key:value的形式作為一個對象,后面的所有參數(shù)都是該節(jié)點的子節(jié)點。
需要注意的是,在JSX語法中,我們不僅有原生的HTML節(jié)點,還有大量的自定義組件,比如:
function Comp() { return '<div>Tom and Jerry</div>' } <Comp></Comp> function Comp() { return '<div>Tom and Jerry</div>'; } React.createElement(Comp, null);
登錄后復(fù)制
可以看出,React.createElement的第一個參數(shù)變成了一個變量,而不是一個字符串,嘗試將函數(shù)Comp首字母小寫:
function comp() { return '<div>Tom and Jerry</div>' } <comp></comp> function comp() { return '<div>Tom and Jerry</div>'; } React.createElement("comp", null);
登錄后復(fù)制
React.createElement的第一個參數(shù)又變成了一個字符串。
這也就是我們在React中寫組件的時候,為什么會要求首字母大寫的原因,Babel在編譯的時候會將首字母小寫的組件視為原生的HTMl節(jié)點進行處理,如果我們將自定義組件首字母小寫,后續(xù)的程序?qū)o法識別這個組件,最終會報錯。
ReactElement
通過Babel編譯后的JS代碼,頻繁出現(xiàn)React.createElement這個函數(shù)。這個函數(shù)的返回值就是ReactElement,通過上面的示例可以看出,React.createElement函數(shù)的入?yún)⒂腥齻€,或者說三類。
-
type
type指代這個ReactElement的類型。
-
字符串比如div,p代表原生DOM,稱為HostComponent
-
Class類型是我們繼承自Component或者PureComponent的組件,稱為ClassComponent
-
方法就是functional Component
-
原生提供的Fragment、AsyncMode等是Symbol,會被特殊處理
-
config
參照上面Babel編譯后的代碼,所有節(jié)點的屬性都會以Key:Value的形式放到config對象中。 -
children
子節(jié)點不止會有一個,所以children不只有一個,從第二個參數(shù)以后的所有參數(shù)都是children,它是一個數(shù)組。
ReactElement的結(jié)構(gòu)是這樣的
const element = { // This tag allows us to uniquely identify this as a React Element $$typeof: REACT_ELEMENT_TYPE, // Built-in properties that belong on the element type: type, key: key, ref: ref, props: props, // Record the component responsible for creating this element. _owner: owner, };
登錄后復(fù)制
它就是一個簡單的對象,為了看清楚這個對象的創(chuàng)建規(guī)則,我們舉個例子。 首先是我們寫的JSX:
<div class='class_name' id='id_name' key='key_name' ref='ref_name'> <span>Tom</span> <span>Jerry</span> </div>
登錄后復(fù)制
它會被Babel編譯為:
React.createElement("div", { class: "class_name", id: "id_name", key: "key_name", ref: "ref_name"}, React.createElement("span", null, "Tom"), React.createElement("span", null, "Jerry"));
登錄后復(fù)制
它會生成這樣一個Element
{ $$typeof: REACT_ELEMENT_TYPE, type:'div', key: 'key_name', ref: "ref_name", props: { class: "class_name", id: "id_name", children: [ React.createElement("span", null, "Tom"), React.createElement("span", null, "Jerry") ] } _owner: ReactCurrentOwner.current,}
登錄后復(fù)制
-
$$typeof 是一個常量,所有通過React.createElement生成的元素都有這個值。一般使用 React 的組件都是掛到父組件的 this.props.children 上面,但是也有例外,比如要實現(xiàn)一個模態(tài)框,就需要將模態(tài)框掛載到body節(jié)點下,這個時候需要使用ReactDOM.createPortals(child, container)這個函數(shù)實現(xiàn),這個函數(shù)生成的$$typeof值就是REACT_PORTAL_TYPE。
-
type指代這個ReactElement的類型
-
key和ref都是從config對象中找到的特殊配置,將其單獨抽取出來,放在ReactElement下
-
props包含了兩部分,第一部分是去除了key和ref的config,第二部分是children數(shù)組,數(shù)組的成員也是通過React.createElement生成的對象,示例中省略了這個步驟。
-
_owner在16.7的版本上是Fiber,F(xiàn)iber是react16+版本的核心,暫時不做深究。
通過這篇文章,我們了解到,JSX中的HTML節(jié)點,在Babel的幫助下,轉(zhuǎn)換為嵌套的ReactElement對象,這些信息對于后期構(gòu)建應(yīng)用的樹結(jié)構(gòu)時非常重要的,而React通過提供這些類型的數(shù)據(jù),來脫離平臺的限制。
推薦學(xué)習(xí):《react視頻教程》