當前位置:
首頁 > 科技 > 比較與理解React的Components,Elements和Instances

比較與理解React的Components,Elements和Instances

比較與理解React的Components,Elements和Instances



前言


今日早讀文章由 攜程@creeperyang授權分享。


正文從這開始~

React是目前(2017.04)流行的創建組件化UI的框架,自身有一套完整和強大的生態系統;同時它也是我目前工作中的主力框架,所以學習和理解React是很自然的需求。


本文在翻譯React Components, Elements, and Instances的基礎上,主要專註理解React的一個核心理念:用Elements Tree描述UI。本文也應該是接下來幾片React相關文章的開頭,所以更合適的標題可能是:


React學習筆記一:Components,Elements和Instances


請注意,閱讀本文最好對React有基本的了解,但React新手也應該可以暢通閱讀。


從JSX 出發


現在我們寫React應用,相當部分都是在寫 JSX。


JSX本身是對JavaScript語法的一個擴展,看起來像是某種模板語言,但其實不是。但正因為形似HTML,描述UI就更直觀了,也極大地方便了開發;你想如果我們沒有HTML,必須手寫一堆的document.createElement(),我想前端們肯定已經崩潰了。


不過如果你一直寫JSX,並且從來沒脫離過JSX,可能某種程度上會阻礙我們理解React。當我們有一個JSX片段,它實際上是調用React API構建了一個Elements Tree:


varprofile=


{[user.firstName,user.lastName].join( )}

;


藉助babel-plugin-transform-react-jsx,上面的JSX將被轉譯成:


varprofile=React.createElement("div",null,


React.createElement("img",{src:"avatar.png",className:"profile"}),


React.createElement("h3",null,[user.firstName,user.lastName].join(" "))


);


那麼,React.createElement是在做什麼?看下相關部分代碼:


varReactElement=function(type,key,ref,self,source,owner,props){


varelement={


// This tag allow 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,


};


// ...

returnelement;


};


ReactElement.createElement=function(type,config,children){


// ...


returnReactElement(


type,


key,


ref,


self,


source,

ReactCurrentOwner.current,


props


);


};


看起來,ReactElement.createElement最終返回了一個對象,這個對象大概是這樣的形狀:


{


type,


key,


props:{


children

}


}


非常明顯,這就是一個Elements Tree!很好,我們知道了react的render方法是返回一個Elements Tree,react的核心就是圍繞Elements Tree 做文章。


下面我們就主要講講Components,Elements(Tree)和Instances,以及三者之間的關係。


傳統面向對象UI編程的痛點:管理實例


如果你是React的新手,那麼之前你可能只接觸過組件的類和實例(component classes and instances )。比如,你可能會


創建一個類來聲明Button組件,當app運行時,屏幕上可能會有多個Button的實例,每個都有自己的屬性和私有狀態。這就是傳統面向對象的UI編程,那麼為什麼要引入Elements 的概念?


傳統UI模型中,你必須自己負責創建和銷毀子組件的實例(child component instances):


每個組件實例必須保存自己的DOM nodes和子組件實例的引用,並在對的時間創建,更新,銷毀它們。代碼的行數將會以可能的狀態的數量的 平方 增長,而且組件可以直接訪問子組件實例將會使解耦變得困難。


那麼,React有什麼不同呢?

React用Elements Tree 描述 UI


An element is a plain object describing a component instance or DOM node and its desired properties.


一個元素(element)就是一個純對象,描述了一個組件實例或DOM node,以及它需要的屬性。它僅僅包含這些信息:組件類型,屬性(properties),及子元素。


元素不是實例,實際上,它更像是告訴React你需要在屏幕上顯示什麼的一種方式。它就是一個有2個數據域(field)的不可變描述對象(immutable description object):


{


type:(string|ReactClass),


props:Object


}


DOM Elements


當元素的type是string時,那麼這個元素就表示一個DOM node(type的值就是tagName,props就是attributes)。 這node就是React將渲染的。比如:

{


type: button ,


props:{


className: button button-blue ,


children:{


type: b ,


props:{


children: OK!


}


}

}


}


將被渲染成:


OK!


注意下元素是怎麼嵌套的。當我們想創建元素樹時,我們設置children 屬性。


注意:子元素和父元素都只是描述,並不是實際的實例。當你創建它們的時候,它們並不指向屏幕上的任何東西。 顯然,它們比DOM輕量多了,它們只是對象。


Component Elements


此外,元素的type也可以是function或者class(即對應的React Component):


{


type:Button,


props:{


color: blue ,


children: OK!


}


}


An element describing a component is also an element, just like an element describing the DOM node. They can be nested and mixed with each other.


這是React的核心idea:一個描述組件的元素同樣是元素,和描述DOM node的元素沒什麼區別。它們可以互相嵌套和混合。


你可以混合搭配DOM和Component Elements:


constDeleteAccount=()=>({


type: div ,


props:{


children:[{


type: p ,


props:{


children: Are you sure?


}


},{


type:DangerButton,


props:{


children: Yep


}


},{


type:Button,


props:{


color: blue ,


children: Cancel


}


}]


}


});


或者,如果你更喜歡 JSX:


constDeleteAccount=()=>(


Are you sure?


Yep


Cancel


);


這種混合搭配幫助組件可以彼此解耦,因為它們可以僅僅通過組合(composition)就能表達is-a和has-a 的關係:


Button是有特定屬性(specific properties)的DOM。


DangerButton是有特定屬性的Button。


DeleteAccount在


里包含了Button和DangerButton。


Components Encapsulate Element Trees


當React碰到type是function|class時,它就知道這是個組件了,它會問這個組件:"給你適當的props,你返回什麼元素(樹)?"。


比如當它看到:


{


type:Button,


props:{


color: blue ,


children: OK!


}


}


React會問Button要渲染什麼,Button 返回:


{


type: button ,


props:{


className: button button-blue ,


children:{


type: b ,


props:{


children: OK!


}


}


}


}


React會重複這種過程,直到它知道頁面上所有的組件想渲染出什麼DOM nodes。


對React組件來說,props是輸入,元素樹(Elements tree)是輸出。


我們選擇讓React來 創建,更新,銷毀 實例,我們用元素來描述它們,而React負責管理這些實例。


Components Can Be Classes or Functions


聲明組件的3 種方式:


class,推薦。


React.createClass,不推薦。


function,類似只有render的class。


Top-Down Reconciliation


ReactDOM.render({


type:Form,


props:{


isSubmitted:false,


buttonText: OK!


}


},document.getElementById( root ));


constForm=({isSubmitted,buttonText})=>{


if(isSubmitted){


// Form submitted! Return a message element.


return{


type:Message,


props:{


text: Success!


}


};


}


// Form is still visible! Return a button element.


return{


type:Button,


props:{


children:buttonText,


color: blue


}


};


};


當你調用ReactDOM.render時,React會問Form組件,給定這些props,它要返回什麼元素。React會以更簡單的基礎值逐漸提煉("refine")它對Form組件的理解,這個過程如下所示:


// React: You told me this...


{


type:Form,


props:{


isSubmitted:false,


buttonText: OK!


}


}


// React: ...And Form told me this...


{


type:Button,


props:{


children: OK! ,


color: blue


}


}


// React: ...and Button told me this! I guess I m done.


{


type: button ,


props:{


className: button button-blue ,


children:{


type: b ,


props:{


children: OK!


}


}


}


}


上面是被React叫做 reconciliation 的過程的一部分。每當你調用ReactDOM.render()或setState()時,都會開始reconciliation過程。在reconciliation結束時,React知道了結果的DOM樹,一個如react-dom或 react-native的renderer會應用必須的最小變化來更新DOM nodes(或平台特定的視圖,如React Native)。


這種漸進式的提煉(refining)過程也是React應用可以容易優化的原因。如果組件樹的某部分大太了,你可以讓React跳過這部分的refining,如果相關props沒有變化。如果props是 immutable 的話,非常容易比較它們是否變化, 所以React可以和 immutability搭配一起並提高效率。


你可能注意到這篇文章講了很多關於組件和元素,卻沒講實例。這是因為相比傳統面向對象的UI框架,在React中實例沒那麼重要。


僅僅以類聲明的組件才有實例,並且你從來不會直接創建它——React為你創建它。儘管有父組件實例訪問子組件實例的機制,但這只是在必要的情況下才使用,通常應該避免。


總結


元素(Element)是React的一個核心概念。一般情況下,我們用React.createElement|JSX來創建元素,但不要以對象來手寫元素,只要知道元素本質上是對象即可。


關於本文


作者:@creeperyang


原文:https://github.com/creeperyang/blog/issues/30


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 前端早讀課 的精彩文章:

高效前端團隊的秘密
程序員拿什麼來學英語
徵集《前端架構設計》書評
JavaScript:少一點條件語句
前端開發轉型產品經理,靠譜嗎?

TAG:前端早讀課 |

您可能感興趣

Area-Weighted Average還是Mass-Weighted Average?總壓的平均方法比較
git reset 和 git revert,git log 和 git reflog 比較
可能是Salesforce與Microsoft Dynamics 365的最全面的比較
Solr與Elasticsearch 之簡明比較
斷路器Netflix OSS Hystrix和Istio的Envoy比較
Dropwizard與Spring Boot比較
chrome,FireFox和Edge性能比較
如何比較twice名井南mina和red velvet裴珠泫的顏
iPhone x和iPhone 8 plus,哪款比較好?
Windows PowerShell進階比較2個csv的差異
這波比較見分曉!藤原浩配色Louis Vuitton x Air Jordan 1完虐Virgil Abloh聯名款!
sync/fsync/fdatasync的簡單比較
API網關性能比較:NGINX vs.ZUUL vs.Spring Cloud Gateway vs.Linkerd
蘋果降價後,iPhone7Plus和iPhone8plus比較,哪個更經濟?
iPhone7和iPhone7 plus選哪個比較好?
singmod 與relu損失函數的比較
觀看比較:勞力士Submariner VS. GMT-Master II
Windows、macOS和Linux三大操作系統使用體驗比較
PCB layout用啥軟體比較好?Cadence or AD?
BeatifulSoup,Xpath,CSS 選擇器的性能比較