在上兩個篇章:初探 JSX 與 JSX 語法中,我們已經了解了 JSX 的由來、功能、內部原理以及語法了。在這個篇章中,我們將要學習如何把 React element 實際 render 到畫面上。
熟悉先讓我們複習一下什麼是 React Element 吧!
React element 就是 React 中,用來表示畫面上元素的物件。
我們可以使用 JSX 或是 React.createElement()
來製作 React Element:
// This is a React Element
const reactElementWithJsx = <h1>Hello React Element</h1>;
// it's equal to
const reactElementWithJs = React.createElement(
'h1',
null,
'Hello React Element'
);
需要注意的一點是,React element 不是真的畫面上的 DOM element,React element 本質上只是一種紀錄 element 資訊 ( e.g. props
、children
) 的 JavaScript Object 而已。
const reactElement = <h1>Hello React Element</h1>;
console.log(reactElement);
// {
// type: "h1",
// props: {
// children: "Hello React Element"
// }
// ...
// }
另外,由於 React element 只是 JavaScript Object,而非真的把資料顯示到 DOM 畫面上,因此創建 React element 的成本遠低於改變 DOM element 的成本。
接下來的問題是,我們要如何 React element 實際顯示到 DOM 上呢?
要把 React element render 到 DOM 上,我們就需要使用 React 的函式 ReactDOM.render
:
ReactDOM.render(element, container[, callback]);
此函式帶有三個參數:
element
:代表我們要 render 的 elementcontainer
:代表要放 element
的 DOM parent element 中,需注意的是 container
是 DOM Element,而非 React Elementcallback
:代表 element
被 render 或被更新後要觸發的動作接著就讓我們來看個使用範例吧!
首先我們會需要在 HTML 檔案中加上一個 element 作為之後的 ReactDOM.render
第二參數:container
使用:
<div id="root"></div>
接著我們的 JSX 檔案中就可以使用 ReactDOM.render
將 element
render 出來了:
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
想看完整範例的話也可以到 React 官方提供的 CodePen 範例 去試試看。
另外,我們也可以使用 ReactDOM.render
來更新 DOM
<div id="root"></div>
const helloWorldElement = <h1>Hello, world</h1>;
const helloReactElement = <h1>Hello, react</h1>;
ReactDOM.render(helloWorldElement, document.getElementById('root'));
setTimeout(() => {
ReactDOM.render(helloReactElement, document.getElementById('root'));
}, 1000);
可以看到一開始 render 出來的 Hello, world
在一秒後會被 React 更新為 Hello, react
。
ReactDOM.render
來更新 DOM 效能較差,並不是最好的做法。這邊只是單純展示 ReactDOM.render
也可以用來更新畫面而已。到這邊為止,我們已經學會了如何使用 React render 出 React Element 了,但對於 React render 的內部運作原理我們還需要再深入了解一下。
當我們使用 ReactDOM.render
後,React 就會幫我們把 React element render 出來,但在實際 render 到畫面之前,React 其實還幫我們做了一些事情。
React 在 render DOM 之前,會先為目前的 React Element(ReactDOM.render
的第一個參數)建立一個快照(snapshot),稱為 Virtual DOM。
這邊講的 snapshot 是一個 JavaScript 樹狀結構,用來仿造 DOM 結構,因此我們把它稱為 Virtual DOM。
如果並非第一次觸發 render 的話,React 就會比較這次的 Virtual DOM 與上次的 Virtual DOM 的差異(Diff)
最後 React 只會把有差異的部分更新到 DOM 上
由上一個段落中我們可以知道,React 只會把畫面上必定會變動的地方更新到 DOM 上,如此就可以降低更新 DOM 的次數。
由於更新 DOM 是一件 I/O 行為,並也會觸發更新畫面的重排(reflow)與重繪(repaint)機制,因此是個極為消耗效能的行為。而 React 把更新 DOM 的次數最小化,不會 rerender 沒有改動的 DOM element,因此就能夠避免掉非必要的效能浪費。
就讓我們用個小範例來驗證此概念吧!
假設我們想做個以秒為單位的小時鐘。
首先,我們同樣會有一個 HTML element 來放置 React element 的 container
節點:
<div id="root"></div>
另外,我們有個 JSX:
// React only update the diffs
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
可以看到此程式中,我們讓 tick
每一秒執行一次。tick
中每秒都會產生新的 React element,並執行 ReactDOM.render
來更新畫面。
接著就讓我們打開 Dev tool 看看 React 到底更新了哪些 DOM element 吧:
從 Dev tool 中我們可以看到,實際上被改變的 DOM element(正在閃爍的部分)只有顯示時間的區塊而已。其他的 element: <h1>Hello, world!</h1>
與 <h2>It is </h2>
的部分都沒有被改變。
由此驗證了 React 只會更新 DOM 上有改動的部分以減少的浪費。
詳細範例也可以到 React 提供的 CodePen 範例 去看。
ReactDOM.render()
通常一個 React app 只會呼叫一次 ReactDOM.render()
而已,並不會拿此函式來更新畫面,也因此,我們放進去的第一個參數常常會是一個代表整個專案的 React element。
在之後的章節中我們會介紹 React 其他更有效率更新 DOM 的方法。
在這個章節中,我們介紹了 React element 為何並介紹了 render React element 的方式:
React element
createElement()
創建Render React element 方式
ReactDOM.render
ReactDOM.render
內部原理
ReactDOM.render
使用技巧
ReactDOM.render
以提高效能