比較與理解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 選擇器的性能比較