React 採用了上一篇文章中所介紹的的 Virtual DOM 概念來實作抽象層,以產生並管理瀏覽器畫面中的真實 DOM。而在 React 中的每一個 Virtual DOM element 稱之為「 React element」,作為這個抽象層中的最小組成單位。
React element 是一種普通的 JavaScript 物件資料,用來描述對應於「真實 DOM」的節點資料與結構。你可以透過 React 提供的 createElement()
方法來建立一個 React element,例如:
import React from "react";
const buttonReactElement = React.createElement(
'button', // 元素類型
{ id: 'button1' }, // 屬性
'I am a button' // 子元素
);
createElement()
的第一個參數為元素類型,第二個參數為屬性,而第三個參數則為子元素。
產生出來的 React element 會是一個普通的 JavaScript 物件,嘗試將其 console.log
印出來之後你會發現它長得大概像這樣:
// buttonReactElement
{
type: 'button',
props: { id: 'button1', children: 'I am a button' },
key: null,
ref: null,
$$typeof: Symbol('react.element'),
};
看起來確實是一個再普通不過的 JavaScript 物件資料,然而將這個 React element 交由 React 進行轉換處理之後,就可以自動產生對應的實際的瀏覽器 DOM 畫面結果:
此時的真實 DOM Tree 中,就可以找到這個由 React 代你自動產生的實際 DOM element:
const buttonDomElement = document.getElementById('button1');
在上面這個範例中:
buttonReactElement
buttonDomElement
當然,與真實的 DOM 一樣,React element 也可以是巢狀的樹狀結構,你可以將一個 React element 的子元素指定成另一個或多個 React element。
而如果子元素不只一個的話,可以繼續在 createElement()
的第四、第五、第六......第 N 個參數以此類推往下填,React 會依序將它們視作接續的子元素:
const reactElement = React.createElement(
'div',
{ id: 'wrapper', className: 'foo' },
React.createElement(
'ul',
{ id: 'list-01' },
// 從第三個參數開始每個參數都是子元素
React.createElement('li', { className: 'list-item' }, 'item 1'),
// 第四個參數就會是第二個子元素
React.createElement('li', { className: 'list-item' }, 'item 2'),
// 第五個參數就會是第三個子元素
React.createElement('li', { className: 'list-item' }, 'item 3'),
),
React.createElement(
'button',
{ id: 'button1' },
'I am a button'
)
);
會產生對應的瀏覽器 DOM 畫面結果:
如上所見,我們可以透過定義 React element 的內容,來間接控制實際上畫面最後產生的 DOM 結構,兩者之間有著顯而易見的完整對應。
但需要注意的是,React element 一旦被建立之後就是不可被事後修改的。為什麼呢?
如同我們在前文有提到過的,在 React 中的 Virtual DOM element 就是指 React element。而為了最佳化 DOM 操作的效能,當畫面即將有更新需求時,我們需要將舊有的 Virtual DOM Tree 結構與新畫面版本的 Virtual DOM Tree 進行比較,才能知道哪些地方是真正需要進行 DOM 更新的。因此如果之前已經建立的舊 React element 會被事後修改或覆蓋的話,不就沒有可靠的歷史依據來跟新的 React element 進行比較了嗎?
因此,React element 從概念上來說就像是在表達「某一個歷史時刻當時的畫面 UI」。所以當我們想要重新描述一個新畫面的結構時,就必須產生一組全新的 React element 來提供給 React,而永遠不會去修改舊有的 React element,以保證這個與歷史結構的比較機制能夠成立並維持。
建議參考資源:
你可能會發現有些 React element 的屬性名稱和格式,與對應的真實 DOM 有些許的不同。這裡列出一些最常見且常用的差異之處:
onclick
⇒ onClick
、tabindex
⇒ tabIndex
...等等aria-*
和 data-*
attribute 則是例外,需要保持全部小寫。舉例來說,aria-label
保持原樣即可class
⇒ className
<label>
會有的 for
屬性 ⇒ htmlFor
style
屬性的內容格式不同:
在 React element 中, style
是以物件的格式指定的,而非像撰寫 HTML Tag 時那樣以字串指定,且其中的 CSS properties 名稱都會改以 camelCase 來表示。
舉例來說:
原本在 HTML 中的 style="font-size: 14px; color: red;"
,在 React element 中則是以 { style: { fontSize: '14px', color: 'red' } }
的形式表示
完整詳細的差異表可以參考官方文件:
https://reactjs.org/docs/dom-elements.html#differences-in-attributes
在經過快要一年的努力後,本系列文的實體書版本推出了~其中新增並補充了許多鐵人賽版本中沒有的脈絡與細節,並以全彩印刷拉滿視覺上的閱讀體驗,現正熱銷中!有興趣的話歡迎參考看看,也歡迎分享給其他有接觸前端的朋友們,非常感謝大家~
《React 思維進化:一次打破常見的觀念誤解,躍升專業前端開發者》
目前首刷的軟精裝版本各大通路已經幾乎都銷售一空,接下來會再刷推出新的平裝版本:
天瓏(平裝版預購):
https://www.tenlong.com.tw/products/9786263337695
博客來(平裝版):
https://www.books.com.tw/products/0010982322
momo(平裝版):
https://www.momoshop.com.tw/goods/GoodsDetail.jsp?i_code=12528845