有 Vue 基础学 React 钩子会快很多。核心差别是Vue 靠「响应式系统自动追踪依赖」React 靠「钩子函数手动声明副作用和缓存」。下面用 Vue 类比把常用钩子一次讲清。一、先建立心智模型Vue Reactref / reactive useStatecomputed useMemowatch / watchEffect useEffectonMounted / onUnmounted useEffect靠依赖数组控制provide / inject useContext模板 ref useRefPinia / Vuex useReducer useContext轻量场景自定义 HookVue 3 useXxx 自定义 HookuseXxx关键区别· Vue数据变了依赖自动重新计算。· React数据变了整个函数组件重新执行你需要用钩子告诉 React「什么该缓存、什么该执行」。二、useState —— 相当于 ref / reactive作用声明一个响应式状态改变它会触发组件重新渲染。Vue 写法script setup import { ref } from vue; const count ref(0); function add() { count.value; } /script template button clickadd{{ count }}/button /templateReact 写法import { useState } from react; function Counter() { const [count, setCount] useState(0); function add() { setCount(count 1); } return button onClick{add}{count}/button; }对比Vue Reactconst count ref(0) const [count, setCount] useState(0)count.value countcount.value setCount(count 1)自动追踪依赖 手动调用 setXxx 触发更新使用场景· 计数器、表单输入、开关状态、列表数据· 任何需要「变了就刷新页面」的数据注意// ❌ 不能直接改 count; // ✅ 用 set 函数 setCount(count 1); // ✅ 依赖旧值时用函数式 setCount(prev prev 1);三、useEffect —— 相当于 watch 生命周期这是 React 最重要的钩子也是 Vue 转 React 最容易懵的。作用处理副作用请求数据、订阅事件、操作 DOM、定时器。三种形态// 1. 每次渲染都执行 useEffect(() { console.log(渲染了); }); // 2. 只在挂载时执行一次相当于 onMounted useEffect(() { console.log(挂载); }, []); // 3. 依赖变化时执行相当于 watch useEffect(() { console.log(count 变了, count); }, [count]);清理函数相当于 onUnmounteduseEffect(() { const timer setInterval(() { console.log(tick); }, 1000); // 组件卸载或依赖变化前执行 return () { clearInterval(timer); }; }, []);Vue 对比script setup import { watch, onMounted, onUnmounted } from vue; onMounted(() { console.log(挂载); }); watch(count, (newVal) { console.log(count 变了, newVal); }); onUnmounted(() { console.log(卸载); }); /scriptuseEffect(() { console.log(挂载); return () console.log(卸载); }, []); useEffect(() { console.log(count 变了, count); }, [count]);使用场景场景 写法请求接口 useEffect(() { fetchData() }, [])监听窗口大小 useEffect(() { window.addEventListener(…) }, [])定时器 useEffect(() { const t setInterval(…); return () clearInterval(t) }, [])依赖某个值变化 useEffect(() {…}, [keyword])操作 DOM useEffect(() { ref.current.focus() }, [])依赖数组规则useEffect(() {...}); // 每次渲染都跑 useEffect(() {...}, []); // 只跑一次 useEffect(() {...}, [a]); // a 变了就跑 useEffect(() {...}, [a,b]); // a 或 b 变了就跑记住依赖数组里用到的外部变量都要写进去否则拿到旧值。四、useMemo —— 相当于 computed作用缓存计算结果依赖不变就不重新计算。Vue 写法script setup import { ref, computed } from vue; const list ref([1, 2, 3]); const total computed(() { console.log(计算了); return list.value.reduce((a, b) a b, 0); }); /scriptReact 写法import { useState, useMemo } from react; function App() { const [list] useState([1, 2, 3]); const total useMemo(() { console.log(计算了); return list.reduce((a, b) a b, 0); }, [list]); return div{total}/div; }对比Vue Reactcomputed(() …) useMemo(() …, [deps])自动追踪依赖 手动写依赖数组值缓存 值缓存使用场景· 复杂计算排序、过滤、统计· 避免每次渲染都重新算· 作为其他 Hook 的依赖时保持引用稳定注意// 简单计算不用 useMemo反而增加开销 const double count * 2; // 直接算就行 // 复杂计算才用 const sorted useMemo(() bigList.sort(...), [bigList]);五、useCallback —— 缓存函数作用缓存函数引用依赖不变就返回同一个函数。为什么需要React 每次渲染都会重新创建函数如果这个函数传给子组件子组件会认为 props 变了而重新渲染。写法import { useState, useCallback } from react; function App() { const [count, setCount] useState(0); const handleClick useCallback(() { console.log(点击); }, []); return Child onClick{handleClick} /; }Vue 对比Vue 里没有直接对应因为 Vue 的响应式系统不需要你手动缓存函数。使用场景· 函数作为 props 传给 React.memo 包裹的子组件· 函数作为其他 Hook 的依赖注意// 大部分情况不需要 useCallback // 只有性能问题出现时才用useMemo 缓存值useCallback 缓存函数。// 等价 useCallback(fn, deps) useMemo(() fn, deps)六、useRef —— 相当于模板 ref 普通变量作用获取 DOM 元素像 Vue 的模板 ref保存不触发渲染的变量获取 DOMVuetemplate input refinputRef / /template script setup import { ref, onMounted } from vue; const inputRef ref(null); onMounted(() { inputRef.value.focus(); }); /scriptReactimport { useRef, useEffect } from react; function App() { const inputRef useRefHTMLInputElement(null); useEffect(() { inputRef.current?.focus(); }, []); return input ref{inputRef} /; }保存变量改了不重新渲染function App() { const countRef useRef(0); function handleClick() { countRef.current; console.log(countRef.current); // 变了但页面不刷新 } return button onClick{handleClick}点击/button; }对比Vue Reactconst el ref(null) 模板 ref“el” const el useRef(null) ref{el}el.value el.currentlet timer 普通变量 useRef 保持跨渲染使用场景· 操作 DOM聚焦、滚动、播放视频· 保存定时器 ID· 保存上一次的值· 保存不需要触发渲染的数据七、useContext —— 相当于 provide / inject作用跨层级传数据不用一层层 props 传。Vue 写法!-- 父 -- script setup import { provide } from vue; provide(theme, dark); /script !-- 深层子 -- script setup import { inject } from vue; const theme inject(theme); /scriptReact 写法import { createContext, useContext } from react; // 1. 创建 Context const ThemeContext createContext(light); // 2. 父组件提供 function App() { return ( ThemeContext.Provider valuedark Child / /ThemeContext.Provider ); } // 3. 深层子组件消费 function Child() { const theme useContext(ThemeContext); return div{theme}/div; }对比Vue Reactprovide(‘key’, value) Context.Provider value{…}inject(‘key’) useContext(Context)按 key 取 按 Context 对象取使用场景· 主题切换· 用户登录信息· 语言国际化· 全局配置八、useReducer —— 相当于简化版 Pinia作用集中管理复杂状态逻辑适合多个状态互相关联。写法import { useReducer } from react; type State { count: number }; type Action { type: add } | { type: sub } | { type: reset }; function reducer(state: State, action: Action): State { switch (action.type) { case add: return { count: state.count 1 }; case sub: return { count: state.count - 1 }; case reset: return { count: 0 }; default: return state; } } function App() { const [state, dispatch] useReducer(reducer, { count: 0 }); return ( div p{state.count}/p button onClick{() dispatch({ type: add })}1/button button onClick{() dispatch({ type: sub })}-1/button button onClick{() dispatch({ type: reset })}重置/button /div ); }对比 PiniaPinia useReducerstate stateactions dispatch(action)直接调用 action 派发 action 对象使用场景· 状态逻辑复杂多个值互相影响· 状态更新逻辑想集中管理· 配合 useContext 做轻量全局状态useState vs useReducer// 简单状态用 useState const [count, setCount] useState(0); // 复杂状态用 useReducer const [state, dispatch] useReducer(reducer, initialState);九、自定义 Hook —— 相当于 Vue 的组合式函数作用把可复用逻辑抽出来名字必须以 use 开头。Vue 写法// useCounter.tsimport{ref}fromvue;exportfunctionuseCounter(initial0){constcountref(initial);constadd()count.value;return{count,add};}React 写法// useCounter.ts import { useState } from react; export function useCounter(initial 0) { const [count, setCount] useState(initial); const add () setCount(c c 1); return { count, add }; } // 使用 function App() { const { count, add } useCounter(0); return button onClick{add}{count}/button; }使用场景· 封装请求逻辑 useRequest· 封装表单 useForm· 封装本地存储 useLocalStorage· 封装窗口大小 useWindowSize常用示例// useLocalStorage function useLocalStorageT(key: string, initial: T) { const [value, setValue] useStateT(() { const saved localStorage.getItem(key); return saved ? JSON.parse(saved) : initial; }); useEffect(() { localStorage.setItem(key, JSON.stringify(value)); }, [key, value]); return [value, setValue] as const; }十、钩子使用规则必须遵守React 钩子有两条铁律只能在函数组件或自定义 Hook 顶层调用// ❌ 不能在条件里 if (cond) { useState(0); } // ❌ 不能在循环里 for (let i 0; i 3; i) { useState(0); } // ✅ 只能在顶层 const [a] useState(0);不能在普通函数里调用// ❌ function helper() { useState(0); } // ✅ 自定义 Hook function useHelper() { useState(0); }原因React 靠调用顺序识别每个 Hook顺序乱了就出错。十一、Vue 转 React 常见误区直接改 state// ❌ count; // ✅ setCount(count 1);以为 setState 是同步的setCount(count 1); console.log(count); // 还是旧值想拿新值setCount(prev { console.log(prev 1); return prev 1; });useEffect 依赖写不全// ❌ count 是旧值 useEffect(() { console.log(count); }, []); // ✅ useEffect(() { console.log(count); }, [count]);对象/数组 state 直接改// ❌ user.name Bob; setUser(user); // ✅ 创建新对象 setUser({ ...user, name: Bob });以为 useEffect 等于 watchuseEffect 是渲染后执行不是数据一变立刻执行且它处理的是副作用不是纯计算。十二、速查表Hook Vue 对应 作用 常用场景useState ref / reactive 声明状态 计数器、表单useEffect watch 生命周期 副作用 请求、订阅、定时器useMemo computed 缓存值 复杂计算useCallback 无 缓存函数 传给子组件的函数useRef 模板 ref / 普通变量 DOM 引用 / 不渲染变量 聚焦、定时器 IDuseContext provide/inject 跨层传值 主题、用户信息useReducer Pinia 简化版 复杂状态 多状态关联自定义 Hook 组合式函数 复用逻辑 请求、表单封装十三、一句话总结· useState声明响应式数据像 ref。· useEffect处理副作用像 watch 生命周期。· useMemo缓存计算结果像 computed。· useCallback缓存函数引用。· useRef拿 DOM 或存不渲染的值。· useContext跨层传值像 provide/inject。· useReducer复杂状态管理像简化版 Pinia。· 自定义 Hook复用逻辑像 Vue 组合式函数。· 两条铁律只在顶层调用只在组件或 Hook 里调用。Vue 是「自动挡」React 是「手动挡」钩子就是你手动控制渲染和副作用的工具。