背景
在vue3项目中,经常会看到hooks文件夹,看起来命名和react hook不可谓不像,有的人会以为这就是所谓的vue hook,接下来让我们去了解一下到底怎么回事儿。
React 的 Hook 和 Vue 中的“Hook”(更准确地说,Vue 3 中的 Composition API 函数,常被称为“组合函数”)都是为了解决逻辑复用、代码组织的问题,但由于框架设计理念不同,它们在语法、使用规则和集成方式上存在差异。下面分别介绍,并说明如何自定义。
一、React 的 Hook
React 中的 Hook 是 React 16.8 引入的特性,核心是让函数组件能够使用状态、生命周期等 React 特性,同时提供了逻辑复用的方案。
1. 核心内置 Hook
useState
:管理组件状态(如计数器、表单值)。useEffect
:处理副作用(如数据请求、DOM 操作、订阅),替代类组件的生命周期(componentDidMount
、componentDidUpdate
、componentWillUnmount
)。useContext
:获取 Context 中的数据,避免 props 层级传递。useReducer
:复杂状态逻辑的管理(类似 Redux 的简化版)。useCallback
/useMemo
:缓存函数/值,优化子组件重渲染。
2. 核心特性
- 只能在函数组件或自定义 Hook 中调用,不能在类组件、条件语句(
if
)、循环(for
)中调用(React 依赖 Hook 调用顺序判断状态归属)。 - 以
use
开头命名(约定,非强制,但便于识别)。 - 逻辑复用:通过自定义 Hook 抽离重复逻辑(如表单处理、数据请求)。
二、Vue 中的“Hook”(Composition API 组合函数)
Vue 3 引入的 Composition API 中,没有“Hook”这一官方术语,但开发者常将基于 Composition API 的逻辑复用函数称为“Vue Hook”或“组合函数(Composables)”。它们的核心是将组件逻辑按功能拆分,而非按选项(data、methods)拆分。
1. 核心内置 API(类似 React Hook 的功能)
- 响应式:
ref
(基础类型响应式)、reactive
(对象响应式)、computed
(计算属性)。 - 生命周期:
onMounted
、onUpdated
、onUnmounted
等(替代 Vue 2 的选项式生命周期)。 - 副作用:
watch
/watchEffect
(监听响应式数据变化)。
2. 核心特性
- 可在
setup
函数或<script setup>
中自由使用,调用位置灵活(可在条件/循环中使用,无严格顺序限制)。 - 命名约定:通常以
use
开头(如useCounter
),但非强制。 - 逻辑组织:按功能(如“用户认证”“数据请求”)而非选项拆分代码,大型组件中更易维护。
三、React Hook 与 Vue 组合函数的核心区别
维度 | React Hook | Vue 组合函数(Composables) |
---|---|---|
调用限制 | 必须在组件/自定义 Hook 顶层调用,不可在条件/循环中使用 | 无严格调用顺序限制,可在条件/循环中使用 |
响应式实现 | 基于 useState 手动管理状态,状态更新需显式调用 setXxx | 基于 ref /reactive 自动响应式,修改直接触发更新 |
与组件的绑定 | 依赖调用顺序与组件关联,状态“绑定”到组件实例 | 响应式数据通过闭包传递,与组件实例松耦合 |
生命周期映射 | useEffect 统一处理所有副作用(需手动清理) | 对应生命周期的独立 API(如 onMounted ) |
四、如何自定义 Hook(分框架)
1. 自定义 React Hook
核心原则:
- 函数名以
use
开头(约定)。 - 内部可调用其他内置 Hook 或自定义 Hook。
- 返回值通常是数组或对象,暴露状态和操作方法。
示例:封装一个“计数器”逻辑
// 自定义 Hook:useCounter.js
import { useState, useCallback } from 'react';
// 接收初始值和步长参数
function useCounter(initialValue = 0, step = 1) {
// 内部使用内置 Hook
const [count, setCount] = useState(initialValue);
// 定义操作方法(用 useCallback 缓存,优化子组件)
const increment = useCallback(() => {
setCount(prev => prev + step);
}, [step]);
const decrement = useCallback(() => {
setCount(prev => prev - step);
}, [step]);
const reset = useCallback(() => {
setCount(initialValue);
}, [initialValue]);
// 返回状态和方法
return { count, increment, decrement, reset };
}
export default useCounter;
在组件中使用:
import useCounter from './useCounter';
function CounterComponent() {
// 复用计数器逻辑
const { count, increment, decrement } = useCounter(0, 2);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+2</button>
<button onClick={decrement}>-2</button>
</div>
);
}
2. 自定义 Vue 组合函数(“Vue Hook”)
核心原则:
- 函数名通常以
use
开头(约定)。 - 内部可使用 Vue 的响应式 API(
ref
、reactive
)和生命周期钩子。 - 返回响应式数据或方法,供组件使用。
示例:封装一个“计数器”逻辑
// 自定义组合函数:useCounter.js
import { ref, computed } from 'vue';
// 接收初始值和步长参数
export function useCounter(initialValue = 0, step = 1) {
// 响应式状态(ref 包装基础类型)
const count = ref(initialValue);
// 计算属性(派生状态)
const doubleCount = computed(() => count.value * 2);
// 操作方法
const increment = () => {
count.value += step; // 直接修改 ref 的 value 属性
};
const decrement = () => {
count.value -= step;
};
const reset = () => {
count.value = initialValue;
};
// 返回响应式数据和方法(组件中可直接解构使用)
return {
count,
doubleCount,
increment,
decrement,
reset
};
}
在组件中使用(<script setup>
语法):
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double: {{ doubleCount }}</p>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
</div>
</template>
<script setup>
import { useCounter } from './useCounter';
// 复用计数器逻辑
const { count, doubleCount, increment, decrement } = useCounter(0);
</script>
五、自定义 Hook 的最佳实践
- 单一职责:一个 Hook 只处理一个逻辑(如“数据请求”“表单验证”),避免过大。
- 可配置性:通过参数让 Hook 更灵活(如请求 Hook 接收 URL、请求方法)。
- 清理副作用:
- React 中,
useEffect
返回清理函数(如取消订阅、中止请求)。 - Vue 中,在
onUnmounted
中清理(如清除定时器、取消请求)。
- React 中,
- 命名清晰:通过名称体现功能(如
useFetch
表示数据请求,useLocalStorage
表示本地存储操作)。
总结
- React Hook 和 Vue 组合函数都是逻辑复用的工具,前者依赖严格的调用规则和显式状态更新,后者更灵活且基于自动响应式。
- 自定义时,两者均遵循“函数封装逻辑,暴露状态/方法”的思路,核心是将重复逻辑抽离,提高代码复用率和可维护性。