iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 3
1
Modern Web

寫React的那些事系列 第 3

React Day3 - Virtual DOM與ReactDOM.render

  • 分享至 

  • xImage
  •  

DOM


寫前端最主要就是針對DOM來操作,DOM就是Document Object Model,它表示了HTML結構並提供API讓Javascript操作。所有在DOM裡的物件都可以稱為node,document本身就是document node,HTML元件就是element node,而我們常用的 document.getElementById 就是給Javascript使用的HTML DOM API。

Javascript藉由操作DOM來改變UI,但是操作DOM的代價是很高的,DOM的改變通常會帶來重排(Reflow)與重繪(Repaint),重排就是重新計算HTML elements的位置或寬高,重繪就是重新顯示HTML elements的顏色...等樣式。通常效能的瓶頸就是在操作DOM上面,Reflow完會再觸發Repaint,所以比單純Repaint要花更上多效能。所以~想要優化網頁效能,最重要的就是減少DOM的操作,或是使用 documentFragment

documentFragment


DocumentFragment也是DOM node,但它不是真的存在DOM tree中,它只存在記憶體中,可以說它是虛擬的DOM,我們可以利用它先把要建立的node append到DocumentFragment中,然後才一次更新到DOM tree上。

假設我們要建立100個div到畫面上

直接操作DOM的寫法 DEMO

var start = new Date().getTime(), end;

for(var i = 0; i < 100; i++) {
  var ele = document.createElement('div');
  end = new Date().getTime();
  $(ele).text('I am Element#' + i + ', ' + ((end - start) / 1000) + "sec");
  $(ele).css({
    background: 'gray',
    padding: 10,
    margin: 10
  });
  $('body').append(ele);
}

end = new Date().getTime();
document.write((end - start) / 1000 + "sec");

使用documentFragment優化的寫法 DEMO

var start = new Date().getTime(),
  end,
  fragment = document.createDocumentFragment();
for(var i = 0; i < 100; i++) {
	var ele = document.createElement('div');
  end = new Date().getTime();
	$(ele).text('I am Element#' + i + ', ' + ((end - start) / 1000) + "sec");
	$(ele).css({
		background: 'gray',
		padding: 10,
		margin: 10
	});
	fragment.appendChild(ele);
}
$('body').append(fragment);

end = new Date().getTime();
document.write((end - start) / 1000 + "sec");

直接從上面兩個DEMO的link中,可以看到執行的秒數,我使用Chrome大概會差兩倍秒數,也可以調整for迴圈次數差異會更顯著。DocumentFragment會一次更新有變動的DOM,只需要reflow一次,所以效能上當然也會更好。

Virtual DOM


React也是用類似documentFragment這樣的概念提供 Virtual DOM 給我們使用,它不是用真實的DOM做操作,而是抽象的DOM,我們不需要自己去處理原本DOM與要更新的DOM之間的差異,Virtual DOM會幫我們做diff運算,這也是使用React上很方便的地方!

如果對Virtual DOM很有興趣,可以再看這篇深度剖析:如何实现一个 Virtual DOM 算法,有深入講解Virtual DOM的做法。

ReactDOM.render()


雖然說Virtual DOM是抽象的DOM,但透過JSX的撰寫方式,其實感覺上並不是真的那麼抽象,因為我們可以直覺的了解到畫面的結構。我們可以透過ReactDOM.render(),把element append到真實的DOM上面。

前面提到的JSX是一個element

const ele = <div>Hello, world!</div>;

要把element顯示在畫面上,首先需要一個實際在HTML上的DOM(element node)

<div id="main"></div>

在這個div裡面的所有element都會由React DOM生成,我們可以用 ReactDOM.render() 來產生

const ele = <div>Hello, world!</div>;
ReactDOM.render(
  ele,
  document.getElementById('main')
);

Demo

當你把React element render出來後,它就會是你定義的樣子,除非重新執行render,或是之後會提到的update props or state,畫面才會再被reflow。

function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(
    element,
    document.getElementById('root')
  );
}

// render once
tick();

而從下面的程式碼,我們可以看出來,除非每次重新呼叫render才會再執行

function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(
    element,
    document.getElementById('root')
  );
}

// render every second
setInterval(tick, 1000);

Demo

參考


The HTML DOM Element Object

React官網 Rendering Elements


上一篇
React Day2 - JSX
下一篇
React Day4 - Component 與 Props
系列文
寫React的那些事31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言