外观
Vue 组合式函数与 React 对比
- 状态:未准备
- 适用方向:前端 / Go 全栈
- 最近复习:待填写
- 下次复习:待安排
一句话回答
Vue composable 用函数封装响应式状态及其副作用,默认每次调用拥有独立状态;与 React Hook 相似的是复用有状态逻辑,依赖追踪和执行模型则不同。
原理解释
Composition API 解决什么?
Options API 按 data、methods、computed、生命周期等选项组织代码;Composition API 可以把同一业务的状态、计算与清理聚在一起,更便于提取复用。它不是强制把所有逻辑写在一个超长 setup 中,也不意味着 Options API 不可用。
可复用逻辑怎样封装?
ts
import { ref, onMounted, onUnmounted } from 'vue'
export function useWindowWidth() {
const width = ref<number | null>(null)
function update() { width.value = window.innerWidth }
onMounted(() => {
update()
window.addEventListener('resize', update)
})
onUnmounted(() => window.removeEventListener('resize', update))
return { width }
}返回包含 ref 的普通对象,调用方 const { width } = useWindowWidth() 可保留响应式。width 初始为 null,让挂载前状态明确,并避免模块或 setup 顶层直接读取 window。这里仍约定在组件 setup 中同步调用,以注册生命周期。
与 React Hook 的对比
| 维度 | Vue Composition API | React 函数组件 / Hooks |
|---|---|---|
| 组件主体 | setup 通常每个实例初始化一次,渲染函数可多次执行 | 组件函数随渲染再次调用 |
| 状态读取 | ref/代理属性追踪依赖 | 本次渲染获得状态快照 |
| 更新表达 | 可通过 ref/代理修改,触发关联订阅 | 使用 setter/dispatch 等请求更新,通常不可变处理对象 |
| 派生值 | computed 自动追踪 getter 内读取 | 常直接计算,必要时用 useMemo 显式依赖 |
| 外部同步 | watch/watchEffect、生命周期 | useEffect/useLayoutEffect 等 |
| 共享逻辑 | composable 默认独立状态 | 自定义 Hook 默认独立状态 |
Vue 也有普通 JavaScript 闭包,可能因提前取值、异步竞态或不恰当解构而读取旧数据,不能说“Vue 完全没有闭包问题”。React 的 ref 则可保存跨渲染可变值,不能把两边差异简化为一方只能使用快照。
场景与边界
composable 适合媒体会话、监听、请求等跨组件复用的逻辑;纯工具函数不需要强行包装成 useXxx。把状态放到模块顶层会改成共享状态,还要考虑 SSR 请求间隔离;复杂共享业务可用 Pinia。参数既可能是值、ref 或 getter 时,可设计为 MaybeRefOrGetter 并在响应式副作用内使用 toValue(Vue 3.3+)读取,避免仅在入口求值一次。
项目中的应用
对照 SimulSpeak 的 useSession 思考 Vue 迁移:哪些资源放在闭包里,哪些状态暴露为 ref,停止与卸载如何复用清理。这里是设计练习,仓库尚无 Vue 迁移实现。
常见追问
- 比 mixin 好在哪里? 依赖与返回值更显式,来源和命名冲突更易管理,便于类型推导与组合。
- 两个组件调用同一个 composable 会共享状态吗? 若状态在函数内部创建,默认不共享;模块变量或外部 store 则可能共享。
- 能把 useEffect 逐行换成 watchEffect 吗? 不宜,先明确依赖、清理、DOM 时机和是否应由用户事件触发。