1. 程式人生 > >Virtual Dom原理淺析

Virtual Dom原理淺析

       React開發人員敦促你在編寫元件時使用一種稱為JSX的語法,混合了HTMLJavaScript。但瀏覽器對JSX及其語法毫無頭緒,瀏覽器只能理解純碎的JavaScript,所以JSX必須轉換成JavaScript。這裡是一個divJSX程式碼,它有一個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,現在我們有一大堆函式呼叫,它的引數會被其他函式呼叫的,或者還有更多的其他函式呼叫這些引數這些帶引數的函式呼叫,是怎麼轉化成組成這個頁面的實體

DOM的呢?為此,我們有一個ReactDOM庫及其它的render方法:

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

他們將在所有進一步的渲染中相互比較,並最終轉化為 真正的DOMvirtual 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!'
    ]
  }
}

 

需要注意的是,那些除了typeattribute以外的屬性,原本是單獨傳進來的,轉換之後,會作為在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>