你的浏览器无法正常显示内容,请更换或升级浏览器!

Vue3 Composition API实战:setup语法糖与hooks设计模式

tenfei
tenfei
发布于2026-07-24 12:06 阅读16次
Vue3 Composition API实战:setup语法糖与hooks设计模式
Vue3的Composition API彻底改变了组件逻辑的组织方式,本文结合实际项目经验讲解setup语法糖、响应式原理、自定义hooks的设计模式与复用技巧,帮你写出更清晰易维护的Vue代码。
# Vue3 Composition API实战:setup语法糖与hooks设计模式 ## 引言 Vue3最大的变化之一就是引入了Composition API。相比Options API,Composition API允许我们将同一个逻辑关注点的代码集中在一起,而不是分散在data、methods、computed等各个选项中。这不仅让大型组件更易维护,也为逻辑复用提供了更优雅的解决方案。本文结合实际项目经验,深入讲解Composition API的使用技巧与自定义hooks的设计模式。 ## 一、setup函数基础与响应式原理 ### 1.1 setup的执行时机 setup是Vue3组件生命周期中最先执行的选项,在beforeCreate之前调用。此时组件实例还未创建,因此无法访问this,但可以正常使用 reactivity APIs。 ```javascript export default { setup() { console.log('setup执行时组件实例还未创建'); // this 为 undefined return { // 这里返回的内容会在模板中可用 }; } }; ``` ### 1.2 响应式数据:ref与reactive Vue3提供了两个创建响应式数据的方式——ref和reactive,各有适用场景。 ```javascript import { ref, reactive } from 'vue'; // ref:适合基本类型,也可包装对象 const count = ref(0); console.log(count.value); // 访问值需要.value // reactive:适合复杂类型(对象/数组),直接代理 const state = reactive({ name: '张三', items: [] }); console.log(state.name); // 直接访问 ``` **避坑提示**:不要对reactive包装过的对象进行解构,解构后变量会失去响应式。使用toRefs可以将reactive对象转为ref的集合。 ## 二、setup语法糖:让代码更简洁 ### 2.1 script setup Vue3.2引入的script setup语法糖是官方推荐的写法,极大简化了代码量: ```vue <script setup> import { ref, computed } from 'vue'; const msg = ref('你好'); const reversed = computed(() => msg.value.split('').reverse().join('')); </script> <template> <div>{{ msg }} → {{ reversed }}</div> </template> ``` 使用script setup后,组件中定义的变量和函数**自动暴露给模板**,无需手动return。 ### 2.2 defineProps与defineEmits 在script setup中,props和emits有更简洁的声明方式: ```vue <script setup> // 带默认值的方式 const props = defineProps({ title: { type: String, required: true }, count: { type: Number, default: 0 } }); const emit = defineEmits(['update', 'delete']); // 使用 emit('update', '新标题'); </script> ``` ## 三、自定义Hooks:Composition API的精髓 ### 3.1 什么是Hooks Hooks本质是一个返回响应式数据的函数。在Vue2时代,mixins是逻辑复用的主要手段,但mixins存在命名冲突和数据来源不清晰的缺点。Hooks完全避免了这些问题。 ```javascript // useWindowSize.js export function useWindowSize() { const width = ref(window.innerWidth); const height = ref(window.innerHeight); const handleResize = () => { width.value = window.innerWidth; height.value = window.innerHeight; }; onMounted(() => window.addEventListener('resize', handleResize)); onUnmounted(() => window.removeEventListener('resize', handleResize)); return { width, height }; } ``` ### 3.2 在组件中使用 ```vue <script setup> import { useWindowSize } from '@/hooks/useWindowSize'; const { width, height } = useWindowSize(); </script> <template> <div>窗口尺寸: {{ width }} x {{ height }}</div> </template> ``` 每个组件调用hooks都会创建独立的响应式状态,组件之间互不影响,这是相比mixins的巨大优势。 ## 四、实战:表单处理Hook ### 4.1 useForm 结合实际项目,以下是一个完整的表单处理hooks: ```javascript import { reactive, ref } from 'vue'; export function useForm(initialValues, rules = {}) { const form = reactive({ ...initialValues }); const errors = reactive({}); const isSubmitting = ref(false); const validate = () => { let valid = true; Object.keys(rules).forEach(field => { const value = form[field]; const rule = rules[field]; if (rule.required && !value) { errors[field] = rule.message || '此字段必填'; valid = false; } else if (rule.pattern && !rule.pattern.test(value)) { errors[field] = rule.message || '格式不正确'; valid = false; } else { errors[field] = ''; } }); return valid; }; const resetForm = () => { Object.keys(initialValues).forEach(key => { form[key] = initialValues[key]; errors[key] = ''; }); }; return { form, errors, isSubmitting, validate, resetForm }; } ``` ### 4.2 使用示例 ```vue <script setup> import { useForm } from '@/hooks/useForm'; const { form, errors, validate, resetForm } = useForm( { username: '', password: '' }, { username: { required: true, message: '用户名不能为空' }, password: { required: true, message: '密码不能为空', pattern: /^[a-zA-Z0-9]{6,}$/ } } ); const handleSubmit = async () => { if (!validate()) return; // 提交表单逻辑... }; </script> ``` ## 五、异步数据获取的最佳实践 ### 5.1 useAsync 在真实项目中,异步数据获取是最常见的场景。以下是一个封装了加载状态、错误处理的hooks: ```javascript import { ref } from 'vue'; export function useAsync(asyncFn, immediate = true) { const data = ref(null); const loading = ref(false); const error = ref(null); const execute = async (...args) => { loading.value = true; error.value = null; try { data.value = await asyncFn(...args); return data.value; } catch (err) { error.value = err; throw err; } finally { loading.value = false; } }; if (immediate) { execute(); } return { data, loading, error, execute }; } ``` ### 5.2 使用示例 ```vue <script setup> import useAsync from '@/hooks/useAsync'; const { data: userList, loading, error, execute: reload } = useAsync( () => fetch('/api/users').then(r => r.json()), false ); onMounted(() => reload()); </script> ``` ## 六、注意事项与常见错误 ### 6.1 避免在setup外访问ref的值 响应式数据必须在setup函数内部或hook内部创建,一旦在setup外访问.value,就会丢失响应式追踪。 ### 6.2 合理拆分Hooks粒度 不要把过多无关逻辑塞进一个hook,保持每个hook职责单一。比如不要写一个useUserAndPosts这样的大杂烩,而是拆成useUser和usePosts。 ### 6.3 依赖注入配合使用 provide/inject在 Composition API中同样适用,适合在多层嵌套组件间传递hooks实例,实现跨层级逻辑共享。 ## 总结 Vue3 Composition API的核心优势在于: 1. **更好的逻辑组织**:同一逻辑关注点的代码集中在一起 2. **更清晰的逻辑复用**:自定义hooks没有命名冲突,状态来源一目了然 3. **更好的TypeScript支持**:类型推断更准确 4. **更小的打包体积**:未使用的API不会被打包 掌握这些技巧,你离Vue高手又近了一步。

2

0

文章点评
Copyright © from 2021 by namoer.com
458815@qq.com QQ:458815
蜀ICP备2022020274号-2