vue3 hooks的结构和作用和具体场景例子
Vue 3 Composition API Hooks 详解
什么是 Vue 3 Hooks?
Vue 3 中的 Hooks(组合式函数)是 Composition API 的核心概念,它们是可重用的逻辑单元,用于封装和复用组件逻辑。与 React Hooks 类似,但基于 Vue 的响应式系统实现。
基本结构
一个典型的 Vue 3 Hook 结构如下:
import { ref, reactive, computed, watch, onMounted } from 'vue';export function useHookName(initialValue) {// 1. 状态声明const state = ref(initialValue);const data = reactive({ ... });// 2. 计算属性const computedValue = computed(() => state.value * 2);// 3. 方法/函数function updateState(newValue) {state.value = newValue;}// 4. 生命周期钩子onMounted(() => {console.log('Hook mounted');});// 5. 监听器watch(state, (newVal, oldVal) => {console.log(`State changed from ${oldVal} to ${newVal}`);});// 6. 返回暴露给组件的APIreturn {state,computedValue,updateState};
}
Hooks 的核心作用
- 逻辑复用:将通用逻辑抽离为独立函数,避免代码重复
- 逻辑组合:将多个相关功能组合在一起,提高代码内聚性
- 关注点分离:将大型组件拆分为基于功能的逻辑单元
- 类型支持:更好地支持 TypeScript 类型推断
- 可测试性:独立于组件的逻辑更易于单元测试
常见场景示例
场景 1: 数据获取 Hook
// useFetch.js
import { ref, onMounted } from 'vue';export function useFetch(url) {const data = ref(null);const error = ref(null);const loading = ref(false);const fetchData = async () => {loading.value = true;try {const response = await fetch(url);data.value = await response.json();} catch (err) {error.value = err;} finally {loading.value = false;}};onMounted(fetchData);return { data, error, loading, refetch: fetchData };
}
组件中使用:
<script setup>
import { useFetch } from './useFetch';const { data, loading, error } = useFetch('https://api.example.com/data');
</script><template><div v-if="loading">Loading...</div><div v-else-if="error">Error: {{ error.message }}</div><div v-else>{{ data }}</div>
</template>
场景 2: 鼠标位置跟踪 Hook
// useMousePosition.js
import { ref, onMounted, onUnmounted } from 'vue';export function useMousePosition() {const x = ref(0);const y = ref(0);const updatePosition = (event) => {x.value = event.clientX;y.value = event.clientY;};onMounted(() => window.addEventListener('mousemove', updatePosition));onUnmounted(() => window.removeEventListener('mousemove', updatePosition));return { x, y };
}
组件中使用:
<script setup>
import { useMousePosition } from './useMousePosition';const { x, y } = useMousePosition();
</script><template><div class="cursor-tracker">Mouse Position: ({{ x }}, {{ y }})</div>
</template>
场景 3: 本地存储 Hook
// useLocalStorage.js
import { ref, watch } from 'vue';export function useLocalStorage(key, defaultValue) {const storedValue = localStorage.getItem(key);const data = ref(storedValue ? JSON.parse(storedValue) : defaultValue);watch(data, (newValue) => {localStorage.setItem(key, JSON.stringify(newValue));}, { deep: true });return data;
}
组件中使用:
<script setup>
import { useLocalStorage } from './useLocalStorage';const theme = useLocalStorage('theme', 'light');function toggleTheme() {theme.value = theme.value === 'light' ? 'dark' : 'light';
}
</script><template><div :class="['app', theme]"><button @click="toggleTheme">切换主题</button><!-- 其他内容 --></div>
</template>
场景 4: 表单处理 Hook
// useForm.js
import { reactive, computed } from 'vue';export function useForm(initialValues, validationRules) {const formData = reactive({ ...initialValues });const errors = reactive({});const validateField = (field) => {const rule = validationRules[field];if (!rule) return true;const isValid = rule(formData[field]);errors[field] = isValid ? '' : `Invalid ${field}`;return isValid;};const validateForm = () => {return Object.keys(validationRules).map(field => validateField(field)).every(result => result);};const isFormValid = computed(() => validateForm());return {formData,errors,validateField,validateForm,isFormValid};
}
组件中使用:
<script setup>
import { useForm } from './useForm';const { formData, errors, isFormValid } = useForm({ email: '', password: '' },{email: value => /^\S+@\S+\.\S+$/.test(value),password: value => value.length >= 6}
);function submitForm() {if (isFormValid.value) {// 提交表单逻辑}
}
</script><template><form @submit.prevent="submitForm"><div><input v-model="formData.email" placeholder="Email"><div class="error">{{ errors.email }}</div></div><div><input v-model="formData.password" type="password" placeholder="Password"><div class="error">{{ errors.password }}</div></div><button type="submit" :disabled="!isFormValid">提交</button></form>
</template>
最佳实践
- 命名约定:使用
use
前缀(如useCounter
) - 单一职责:每个 Hook 只关注一个特定功能
- 响应式返回:返回 ref 或 reactive 对象保持响应性
- 参数处理:使用
unref
处理可能的 ref 参数 - 组合 Hook:可以组合多个简单 Hook 构建复杂逻辑
- 副作用清理:在
onUnmounted
中清理事件监听器、定时器等
import { unref } from 'vue';// 处理可能为 ref 的参数
export function useHook(param) {const unwrappedParam = unref(param);// ...
}
对比 Options API
特性 | Options API | Composition API (Hooks) |
---|---|---|
代码组织 | 按选项类型分组 | 按逻辑功能分组 |
逻辑复用 | Mixins/作用域插槽 | 组合式函数 |
类型支持 | 有限 | 优秀的 TypeScript 支持 |
学习曲线 | 较低 | 较高(需要理解响应式原理) |
逻辑复杂度处理 | 复杂组件中困难 | 更容易管理复杂逻辑 |
代码可读性 | 分散在各选项中 | 相关逻辑集中 |
总结
Vue 3 Hooks 通过 Composition API 提供了强大的逻辑封装和复用能力。它们允许开发者:
- 将组件逻辑拆分为更小、更专注的函数
- 在多个组件之间轻松共享和复用逻辑
- 更清晰地组织复杂组件的代码
- 获得更好的 TypeScript 支持
通过将业务逻辑提取到自定义 Hook 中,可以创建更简洁、更可维护的 Vue 应用程序。随着项目规模的增长,这种基于组合的开发模式会显示出越来越大的优势。