博客 / 詳情

返回

從底層到實踐:深度解析 Vue Composition API 與 React Hooks 的異同

一、設計哲學與底層原理差異

1.1 響應式系統的基因差異

Vue3 的 Composition API 建立在 Proxy-based 響應式系統之上,通過劫持對象的 getter/setter 實現依賴收集。當訪問響應式對象時,Vue 會自動建立組件與數據的依賴關係。

// Vue 響應式原理簡版實現
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      track(target, key) // 依賴收集
      return Reflect.get(target, key)
    },
    set(target, key, value) {
      Reflect.set(target, key, value)
      trigger(target, key) // 觸發更新
    }
  })
}

React Hooks 基於函數組件的閉包機制和狀態隊列實現。每次渲染都會捕獲當前閉包中的狀態值,通過鏈表結構維護 Hook 的順序:

// React 狀態隊列簡版實現
let hookIndex = 0
let hooks = []

function useState(initial) {
  const currentIndex = hookIndex++
  hooks[currentIndex] = hooks[currentIndex] || initial
  
  const setState = (newValue) => {
    hooks[currentIndex] = newValue
    scheduleUpdate() // 觸發重新渲染
  }
  
  return [hooks[currentIndex], setState]
}

1.2 更新機制的差異

  • Vue 採用精確的依賴追蹤,只有真正被使用的數據變化才會觸發組件更新
  • React 依賴狀態變化的顯式通知,當狀態變更時總是觸發整個組件重新渲染

1.3 生命週期管理

  • Vue 的 setup() 函數在組件實例創建時執行一次,與組件生命週期解耦
  • React Hooks 在每次渲染時都會執行,需要依賴數組控制執行時機

二、實踐中的核心差異

2.1狀態管理的不同範式Vue的響應式系統允許直接修改狀態:

// Vue
const count = ref(0)
const increment = () => count.value++
React強調不可變性:
// React
const [count, setCount] = useState(0)
const increment = () => setCount(prev => prev + 1)

2.2副作用處理的對比Vue的watchEffect自動追蹤依賴:

// Vue
watchEffect(() => {
  console.log(`Count changed: ${count.value}`)
})
React需要顯式聲明依賴:
// React
useEffect(() => {
  console.log(`Count changed: ${count}`)
}, [count])

2.3邏輯複用模式對比

Vue複用:

// useCounter.js
export function useCounter() {
  const count = ref(0)
  const increment = () => count.value++
  return { count, increment }
}

React自定義Hook:

// useCounter.js
export function useCounter() {
  const [count, setCount] = useState(0)
  const increment = () => setCount(p => p + 1)
  return { count, increment }
}

三、典型場景對比分析

3.1 複雜狀態管理

Vue 組合式方案:
function useUser() {
  const user = reactive({
    name: '',
    age: 0,
    // 計算屬性
    isAdult: computed(() => user.age >= 18)
  })

  async function loadUser() {
    const data = await fetchUser()
    Object.assign(user, data)
  }

  return { user, loadUser }
}
React 實現方案:
function useUser() {
  const [user, setUser] = useState({ name: '', age: 0 })
  const isAdult = useMemo(() => user.age >= 18, [user.age])

  const loadUser = useCallback(async () => {
    const data = await fetchUser()
    setUser(data)
  }, [])

  return { user, isAdult, loadUser }
}

3.2 DOM 操作場景

Vue 的 ref 模板引用:
const inputRef = ref(null)

onMounted(() => {
  inputRef.value.focus()
})

return { inputRef }
React 的 useRef:
const inputRef = useRef(null)

useEffect(() => {
  inputRef.current.focus()
}, [])

return <input ref={inputRef} />

四、深度設計對比

4.1 心智模型差異

  • Vue 採用 "狀態 + 依賴追蹤" 模型,開發者關注數據變化
  • React 採用 "狀態快照 + 渲染函數" 模型,開發者關注狀態到 UI 的映射

4.2 更新粒度控制

  • Vue 的響應式系統實現組件級精準更新
  • React 需要配合 memo、useMemo 等手動優化

4.3 TypeScript 支持

  • Vue 的響應式系統能自動推導 ref.value 類型
  • React 需要顯式聲明泛型類型參數

4.4 Hook 調用順序的約束與自由

ReactHooks的鏈表實現機制 \
React Hooks 通過維護一個隱式的鏈表來跟蹤 Hook 的調用順序。每次組件渲染時,Hooks 必須按完全一致的順序 被調用,否則會導致狀態錯亂。這一設計直接導致以下限制:
// React 錯誤示例:條件中調用 Hook
function MyComponent({ isAdmin }) {
  if (isAdmin) {
    const [adminData, setAdminData] = useState(null); // 違反規則
  }
  const [userData, setUserData] = useState({});
  // ...
}

此處條件語句中的 Hook 調用會導致後續渲染時 Hook 順序不一致,引發難以追蹤的 Bug。

VueCompositionAPI的響應式解耦 \
Vue 的響應式系統基於 Proxy 的依賴收集,與 Hook 調用順序完全解耦。setup 函數僅在組件實例化時執行一次,狀態通過閉包持久化:
// Vue 合法代碼:條件中使用 ref
export default {
  setup(props) {
    if (props.isAdmin) {
      const adminData = ref(null); // 完全合法
    }
    const userData = ref({});
    return { userData };
  }
}

Vue 的響應式標記在初始化階段即完成,後續狀態變化通過依賴觸發更新,無需維護調用順序。

五、性能優化的不同路徑

5.1 渲染機制對 GC 的影響

React的渲染週期壓力
每次重新渲染時,函數組件內的所有 Hook 都需要重新執行,導致:

  • 臨時閉包對象頻繁創建/銷燬
  • 依賴數組的淺比較帶來額外計算
  • 需要手動優化(如 useMemo/useCallback)來減少子組件渲染

Vue的響應式優化
基於 Proxy 的響應式系統實現精準更新:

// Vue 自動依賴追蹤示例
const state = reactive({ a: 1, b: 2 });

watchEffect(() => {
  console.log(state.a); // 僅當 a 變化時觸發
});

當 state.b 變化時,不會觸發上述副作用。這種細粒度更新顯著降低 GC 壓力,尤其在複雜組件中優勢明顯。
 

六、框架哲學總結

維度 Vue Composition API React Hooks
設計基礎 響應式系統 函數閉包機制
狀態更新 可變狀態 不可變狀態
依賴管理 自動追蹤 顯式聲明
生命週期 與實例綁定 與渲染週期綁定
邏輯複用 組合式函數 自定義 Hook
類型推導 基於響應式結構 泛型參數
更新粒度 組件級 + 精準更新 組件級 + 手動優化
適用場景 複雜響應式應用 聲明式UI + 不可變數據流

七、總結:

差異背後的統一思想盡管存在顯著差異,兩者都致力於解決同類問題

  • 邏輯複用 :通過組合取代繼承(React)和混入(Vue)
  • 關注點分離 :按功能而非生命週期組織代碼
  • 類型支持 :更好的 TypeScript 集成

最終選擇取決於:

  • 團隊偏好 :響應式思維 vs 函數式思維
  • 項目規模 :Vue 適合快速迭代,React 適合嚴格架構
  • 性能需求 :高頻更新場景優選 Vue,複雜渲染控制優選 React

技術交流溝通➕V:yinzhixiaxue

user avatar zzd41 頭像 dujing_5b7edb9db0b1c 頭像 esunr 頭像 chongdianqishi 頭像 flymon 頭像 susouth 頭像 weirdo_5f6c401c6cc86 頭像 gaoming13 頭像 tingzhong666 頭像 liyl1993 頭像 b_a_r_a_n 頭像 user_p5fejtxs 頭像
48 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.