許多初學者在剛接觸 React 時,常會產生一種錯覺:「React 好難,框架語法好多、邏輯好複雜。」
但如果我們靜下心來將 React 剝開,就會發現:React 本質上只是一個 JavaScript 函式庫 (UI Library)。你在 React 中遇到的大部分瓶頸,其實都不是 React 的問題,而是對 JavaScript 的底層機制理解不夠透徹。
我們先不談複雜的 API 語法,而是先回到最初,聊聊 React 的誕生背景與最關鍵的核心心智模型:UI = f(state)。
在 React 誕生之前(大約 2010 年前後),前端主流開發方式是使用 原生 JavaScript 或 jQuery。這種開發模式被稱為「命令式編程 (Imperative Programming)」。
所謂的命令式編程,意味著你必須一步步親自告訴瀏覽器該「如何做 (How)」:
看一個簡單的計數器例子:
// 傳統 jQuery 寫法
let count = 0;
$('#btn-add').on('click', function() {
// 1. 修改資料
count++;
// 2. 手動更新 DOM 畫面
$('#count-display').text(count);
// 3. 根據資料手動控制 DOM 狀態與樣式
if (count > 10) {
$('#count-display').addClass('warning');
}
});
傳統寫法帶來的兩大問題:
狀態與 DOM 強烈耦合 (Coupling)
畫面上的文字、Class 狀態與記憶體中的 count 變數被分散在各個事件監聽器中。當專案規模變大、邏輯變複雜時,你很難知道「目前的 DOM 狀態到底是由哪一段程式碼修改的」。
狀態同步災難 (State Sync Bug)
一旦忘記手動更新某個 DOM 節點,或是更新順序出錯,就會造成「記憶體裡的資料是 11,但畫面上顯示的卻是 10」的資料不同步問題。
2013 年,Facebook (Meta) 開源了 React,帶領前端工程界進行了一次偉大的「典範轉移 (Paradigm Shift)」。
React 引入了「聲明式編程 (Declarative Programming)」:你不再需要手動操作 DOM,而是只需要描述「畫面在特定資料狀態下應該長什麼樣子 (What)」,DOM 的實際更新細節則全權交給 React 自動處理。
將剛才的計數器改用 React 來寫:
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<span className={count > 10 ? 'warning' : ''}>
{count}
</span>
<button onClick={() => setCount(count + 1)}>加一</button>
</div>
);
}
在 React 中:
完全沒有寫過 document.getElementById 或 innerHTML。
只需要更新 count(State),畫面就會自動同步更新。
三、 React 的核心心智模型:UI = f(state)要學好 React,最重要的一件事就是建立正確的心智模型(Mental Model)。React 最靈魂的核心公式莫過於:UI = f(state)
這個公式說明了一切:
state(狀態):應用程式在某個時間點的資料與記憶體狀態。
f(組件 / Component):一個純粹的函數。
UI(使用者介面):函數接收 state 執行後產生的畫面成果。
在傳統 DOM 開發中,畫面是持續被增量修改(Mutation)的物件;
而在 React 裡,畫面是資料在某個特定時間點的「快照 (Snapshot)」。當資料(State)發生改變時,React 會重新執行這個函數 f,傳入新的 State,並算出新的 UI 快照。接著透過 Virtual DOM 與 Diffing 演算法,比對新舊快照之間的差異,最後只精準更新瀏覽器上真正改變的 DOM 節點。
既然 React 的核心是 UI = f(state),這意味著:React 組件本質上就是 JavaScript 函數!許多人在學習 React 時覺得卡關,往往不是因為 React 的 API 太多(常見的 API 其實只有十來個),而是因為對 JavaScript 的語言特性 缺乏深刻的理解。例如:
如果你不了解 JS 的底層機制,寫 React 時就會像在碰運氣,不斷被各種莫名其妙的 Re-render 或無窮迴圈折磨。