Virtual Dom原理淺析
React開發人員敦促你在編寫元件時使用一種稱為JSX的語法,混合了HTML和JavaScript。但瀏覽器對JSX及其語法毫無頭緒,瀏覽器只能理解純碎的JavaScript,所以JSX必須轉換成JavaScript。這裡是一個div的JSX程式碼,它有一個class name和一些內容:
<div className='cn'>
Content!
</div>
以上的程式碼,被轉換成“正經”的JavaScript程式碼,其實是一個帶有一些引數的函式呼叫:
React.createElement( 'div', { className: 'cn' }, 'Content!' );
讓我們仔細看看這些引數。
- 第一個是元素的type。對於HTML標籤,它將是一個帶有標籤名稱的字串。
- 第二個引數是一個包含所有元素屬性(attributes)的物件。如果沒有,它也可以是空的物件。
- 剩下的引數都可以認為是元素的子元素(children)。元素中的文字也算作一個child,是個字串’Content!’ 作為函式呼叫的第三個引數放置。
你應該可以想象,當我們有更多的children時會發生什麼:
<div className='cn'> Content 1! <br /> Content 2! </div>
React.createElement(
'div',
{ className: 'cn' },
'Content 1!', // 1st child
React.createElement('br'), // 2nd child
'Content 2!' // 3rd child
)
把元件(components)組合成頁面(page)
所以,我們已經將所有JSX元件轉換為純JavaScript,現在我們有一大堆函式呼叫,它的引數會被其他函式呼叫的,或者還有更多的其他函式呼叫這些引數…這些帶引數的函式呼叫,是怎麼轉化成組成這個頁面的實體
function Table({ rows }) { /* ... */ } // defining a component
// rendering a component
ReactDOM.render(
React.createElement(Table, { rows: rows }), // "creating" a component
document.getElementById('#root') // inserting it on a page
);
當ReactDOM.render被呼叫時,React.createElement最終也會被呼叫,返回以下物件:
// There are more fields, but these are most important to us
{
type: Table,
props: {
rows: rows
},
// ...
}
這些物件,在React的角度上,構成了虛擬DOM。
他們將在所有進一步的渲染中相互比較,並最終轉化為 真正的DOM(virtual VS real, 虛擬DOM VS 真實DOM)。
下面是另一個例子:這次div有一個class屬性和幾個children:
React.createElement(
'div',
{ className: 'cn' },
'Content 1!',
'Content 2!',
);
變成:
{
type: 'div',
props: {
className: 'cn',
children: [
'Content 1!',
'Content 2!'
]
}
}
需要注意的是,那些除了type和attribute以外的屬性,原本是單獨傳進來的,轉換之後,會作為在props.children以一個數組的形式打包存在。也就是說,無論children是作為陣列還是引數列表傳遞都沒關係 —— 在生成的虛擬DOM物件的時候,它們最後都會被打包在一起的。
進一步說,我們可以直接在元件中把children作為一項屬性傳進去,結果還是一樣的:
<div className='cn' children={['Content 1!', 'Content 2!']} />
在構建虛擬DOM物件完成之後,ReactDOM.render
將會按下面的原則,嘗試將其轉換為瀏覽器可以識別和展示的DOM節點:
- 如果type包含一個帶有String型別的標籤名稱(tag name)—— 建立一個標籤,附帶上props下所有attributes。
- 如果type是一個函式(function)或者類(class),呼叫它,並對結果遞迴地重複這個過程。
- 如果props下有children屬性 —— 在父節點下,針對每個child重複以上過程。
最後,得到以下HTML(對於我們的表格示例):
<table>
<tr>
<td>Title</td>
</tr>
...
</table>