軟體工程師職涯升級計畫啟動!立即預約職涯諮詢、履歷健檢或模擬面試👈,為您的加薪做好準備!
你是否曾經寫過一個 jQuery 專案,然後在一堆 callback、DOM 操作與邏輯糾纏中迷失方向?我也是。直到我遇見了 React,特別是 Hooks,才真正理解什麼是可維護、可擴展的前端架構。
🧮 一個簡單的計數器範例
從一個簡單的需求開始:點擊按鈕,數字會 +1。
jQuery 實作
html
複製編輯<!-- index.html -->
<button id="counter">0</button>
js這段程式碼的流程如下:
複製編輯// index.js
$('#counter').on('click', function() {
const current = parseInt($(this).html(), 10)
$(this).html(current + 1)
})
- 讀取當前狀態值:
$(this).html()
- 計算新值
- 更新 DOM
雖然簡單直觀,但隨著狀態變多、邏輯變複雜,jQuery 的管理方式就開始吃力了。
React Class Component 實作
jsx
複製編輯// Counter.js
import React from "react"
export class Counter extends React.Component {
constructor(props) {
super(props)
this.state = { count: 0 }
}
increment = () => {
this.setState({ count: this.state.count + 1 })
}
render() {
return (
<button onClick={this.increment}>
{this.state.count}
</button>
)
}
}
html
複製編輯<!-- index.html -->
<div id="root"></div>
<script type="text/babel">
ReactDOM.render(<Counter />, document.getElementById('root'))
</script>
React 的重點在於 狀態 (state) 和 畫面 (UI) 的同步:只要更新 state
,畫面就會自動重繪。
🔄 React 如何簡化 DOM 操作?
在 jQuery 中你得手動更新 DOM:
js
複製編輯$('#counter').html(nextCount)
而 React 中,只需要更新 state:
jsx
複製編輯setCount(count + 1)
React 會透過 Virtual DOM 比對差異並高效更新畫面。
這背後的理念是:UI 是 state 的投影 (UI = f(state))
🧩 元件化、封裝與維護性
jQuery 的問題
在 jQuery 中,邏輯分散在不同檔案,難以追蹤。例如:
html
複製編輯<!-- index.html -->
<button id="submitBtn">送出</button>
但行為邏輯可能在某個 .js
裡頭,找不到源頭也無從追蹤錯誤。
React 的組件化
React 把「樣板 + 行為」整合在同一個元件中,實現:
- 封裝性:每個元件管理自己的狀態與邏輯。
- 可重用性:元件可在不同頁面重複使用。
- 可維護性:小型元件容易測試與理解。
🔁 重繪與 Virtual DOM:React 效能優化的關鍵
在使用 jQuery 等傳統 DOM 操作工具時,每當 UI 需要更新,我們都會直接操作實體 DOM(Real DOM)。這樣的方式在簡單應用上沒什麼問題,但在複雜應用中,頻繁的 DOM 操作將會大大拖慢效能。原因在於 DOM 操作非常昂貴,會觸發瀏覽器的重排(Reflow)與重繪(Repaint)。
為了解決這個問題,React 採用了 Virtual DOM(虛擬 DOM) 的機制。它的基本概念是:
- React 不直接操作實體 DOM,而是先建立一棵Virtual DOM 樹(記憶體中的 UI 表現)
- 當 state 改變時,React 會根據新的 state 生成一棵新的 Virtual DOM 樹
- 接著,React 會比較新舊兩棵 Virtual DOM 的差異(diffing)
- 最後,只針對需要變更的部份去更新實體 DOM(這個步驟稱為 reconciliation)
🔬 小範例說明:
假設你有一個簡單的列表 UI:
jsx
複製編輯function List({ items }) {
return (
<ul>
{items.map(item => <li key={item.id}>{item.text}</li>)}
</ul>
);
}
每次新增一個項目時,React 並不是將整個 <ul>
和全部 <li>
都重新渲染,而是:
- 比對前後的 Virtual DOM
- 偵測出「只有最後一個
<li>
是新增的」 - 然後只更新新增的那個節點到實體 DOM
這種差異比較 + 精準更新的做法,比 jQuery 直接操作整個 DOM 要高效非常多。
🧠 React Hooks:為什麼它會顛覆你的開發模式?
問題:Class Component 的混亂邏輯
js
複製編輯componentDidMount() {
document.title = `Clicked ${this.state.count}`
ChatAPI.subscribeToFriendStatus(this.props.friend.id, this.handleStatusChange)
}
常見狀況:
- 不同邏輯被混在一個方法裡(如:設定
document.title
+ 訂閱事件) - 相同邏輯被拆散(如:初始化和清除邏輯分散在
DidMount
與WillUnmount
)
這讓程式碼難以理解、維護、測試。
Hooks 解法:關注點分離
jsx
複製編輯function FriendStatusWithCounter({ friend }) {
const [count, setCount] = useState(0)
const [isOnline, setIsOnline] = useState(null)
useEffect(() => {
document.title = `Clicked ${count} times`
}, [count])
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline)
}
ChatAPI.subscribeToFriendStatus(friend.id, handleStatusChange)
return () => {
ChatAPI.unsubscribeFromFriendStatus(friend.id, handleStatusChange)
}
}, [friend.id])
return (
<div>
<p>{friend.name} is {isOnline ? 'Online' : 'Offline'}</p>
<button onClick={() => setCount(count + 1)}>{count}</button>
</div>
)
}
重點:
- 每段邏輯用一個
useEffect
,清晰可拆測 - 不需要
this
,不需要bind
- 更貼近 Functional Programming 思維
🧩 自訂 Hook:邏輯重用再進化
js
複製編輯function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null)
useEffect(() => {
const handleStatusChange = status => setIsOnline(status.isOnline)
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange)
return () => ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange)
}, [friendID])
return isOnline
}
使用時只要:
js
複製編輯const isOnline = useFriendStatus(friend.id)
Hook 讓你不需要改變元件層級,就能重用有狀態的邏輯。
🧭 Hooks 優缺點比較
✅ 優點
- 不再依賴 lifecycle 方法管理副作用
- 更符合 Functional Paradigm
- 可將邏輯封裝成可重用的 Hook
- 新手學起來比 class 更直覺(不用理解
this
)
❗ 缺點
useEffect
容易誤用(沒設依賴陣列會導致無窮觸發)- 大型應用需注意效能與重繪次數
- 過度抽象的 Hook 也可能變得難維護
🎥 推薦資源
🧾 結語
React 的出現,並不是因為 jQuery 不好,而是前端需求與複雜度提高後,我們需要新的解法。
Hook 是 React 的一次里程碑,它重新定義了狀態邏輯、組件設計與程式碼組織方式。如果你還停留在 jQuery 或 class-based 的 React,現在是一個很棒的時機去認識 Hook!