搜尋全站文章

沒有找到相關文章

試試其他關鍵字或檢查拼寫

找到 0 篇文章 • 包含部落格、筆記、旅遊文章

Medium GitHub LinkedIn

React 與單向資料流

作者頭像
Sam

最近發佈

19 分鐘閱讀

React 與單向資料流

前言

React 與 無前端框架的差別:Virtual DOM

1. 較好維護的程式碼:

如果使用傳統的 Javascript 來為維護 DOM,這是一件很麻煩的事情:

  • 產生一個初始的 <ul/> 物件。
  • 當按鈕被點擊時擷取使用者輸入的資訊。
  • 產生一個新的<li/>物件,並且賦予 id、內容。

Virtual DOM 簡化整個流程。

2. 只對必要重繪的部分進行重構:

當 DOM 元素進行變更時,Virtual DOM 會比較上一次的節點,並對對小的所需進行重繪。想像今天有個 Counter 的物件,當點擊按鈕後會更新計數器的數量:

const virtualDOM = { type: "div", props: { children: [ { type: "p", props: { children: ["目前計數:0"], }, }, { type: "button", props: { onClick: () => setCount(count + 1), children: ["點我增加"], }, }, ], }, };

然而當按鈕被點擊後,計數器的數字被變更,React 紀錄了新的資料對應關係(而非真實的 DOM 元素),並產生了新的 virtaulDOM:

const newVirtualDOM = { type: "div", props: { children: [ { type: "p", props: { children: ["目前計數:1"], }, }, { type: "button", props: { onClick: () => setCount(count + 1), children: ["點我增加"], }, }, ], }, };

透過 React 的演算法去比較新/舊的結構的差異,我們發現僅有 <p/> 的內容需要重繪,React 會根據最小的 DOM 操作範圍,去返回新的真實 DOM 元素物件:

<div> <p>目前計數:1</p> <button>點我增加</button> </div>

若操作 DOM 元素,可能會重置整個物件並進行畫面重繪,相較於 Javascript 的計算是一件昂貴的事情。(這邊較為抽象,暫時參考網路上的說法)。

註:在 React 當中,畫面繪製分為兩個流程:

  • Reconciler:負責定義、管理畫面結構的描述(如上面的 newVirtualDOM)。
  • Renderer:根據 Reconciler 的定義,同時會比較新舊的差異,並在 react-dom 這個容器中繪製真實的 DOM 元素。

React DOM 的容器:Root

若使用 create-react-app 建立一個專案,你可以在資料夾中找到 index.html,長相如下:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/x-icon" href="/favicon.png" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>React + TypeScript + Replit</title> </head> <body> <div id="root"></div> <script type="module" src="/src/index.jsx"></script> </html>

在同一個資料夾也會發現index.js

import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; ReactDOM.createRoot(document.getElementById("root")).render( <React.StrictMode> <App /> </React.StrictMode> );

想像網頁在建構的過程中,我們會透過 ReactDOM 這個代理人,來幫我們決定該繪製什麼畫面。

什麼是React Element

在 React 當中,我們常以 Self Closing Tag 呼叫一個物件(例如: <Greeting/>),事實上在編譯時呼叫了 React.createElement() 這個方法去建立 virtaul DOM 的節點:

function Greeting({ name }) { return createElement( "h1", // HTML標籤 { className: "greeting" }, //屬性 "Hello" // 子元素 ); }

而這些微小的解點構成了一個頁面的樣子,同時在 Component 上能夠描述各式的標籤:

function App() { return ( <div> <Greeting name="Amy" /> <Greeting name="Sam" /> <Greeting name="Johnny" /> </div> ); }

然而我們的瀏覽器或編譯器並不真的認識 <Greeting/> 這個標籤,而是在 build time 時,透過轉譯器(如:Babel),將 <Greeting/> 轉譯成:

createElement(’hi’, {className: “greeting”}, "Hello")

並在瀏覽器運行時(run time)運作這些方法:

createElement(’hi’, {className: “greeting”}, "Hello") createElement(’hi’, {className: “greeting”}, "Hello") createElement(’hi’, {className: “greeting”}, "Hello") ...(假如有很多節點的話)

參考資料

  1. React 思維進化:一次打破常見的觀念誤解,躍升專業前端開發者