setup 函数详解
setup 函数是 Vue 3 Composition API 的核心入口,它为组件提供了一个统一的起点,用于定义响应式数据、计算属性、方法和生命周期钩子。本节将深入讲解 setup 函数的定义、用法、参数和注意事项,并通过示例展示如何在实际开发中使用它。
什么是 setup 函数?
setup 是 Vue 3 中新增的一个组件选项,专门用于 Composition API。它在组件的生命周期中位于 beforeCreate 和 created 之间执行,是组件逻辑的集中点。
基本定义
<template>
<p>{{ count }}</p>
<button @click="increment">增加</button>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const count = ref(0);
const increment = () => {
count.value++;
};
return { count, increment }; // 返回给模板使用
}
};
</script>
- 作用:定义组件的响应式数据和方法,并通过返回对象暴露给模板。
- 执行时机:在组件实例创建之前运行,取代了
data和methods的部分功能。
setup 的参数
setup 函数接收两个参数:props 和 context,提供组件的外部输入和上下文信息。
1. props 参数
props 是组件接收的外部属性,与 Options API 的 props 一致。
示例:使用 props
<template>
<div>
<p>问候:{{ greeting }}, {{ name }}!</p>
</div>
</template>
<script>
export default {
props: {
greeting: {
type: String,
default: 'Hello'
},
name: {
type: String,
required: true
}
},
setup(props) {
// props 是响应式的
return { greeting: props.greeting, name: props.name };
}
};
</script>
- 用法:在父组件传入
<Child greeting="Hi" name="Alice" />时,显示“问候:Hi, Alice!”。 - 注意:
props是只读的,不能直接修改。
2. context 参数
context 是一个对象,包含 attrs、slots、emit 等上下文信息。
示例:使用 emit
<template>
<button @click="notifyParent">通知父组件</button>
</template>
<script>
export default {
setup(props, context) {
const notifyParent = () => {
context.emit('custom-event', 'Hello from child'); // 触发自定义事件
};
return { notifyParent };
}
};
</script>
- 父组件用法:
<Child @custom-event="handleEvent" /> <script> export default { methods: { handleEvent(message) { console.log(message); // 输出 "Hello from child" } } }; </script> - context 属性:
attrs:非 Prop 的特性。slots:插槽内容。emit:触发自定义事件。
解构 context
setup(props, { emit }) {
const notify = () => emit('custom-event', 'Data');
return { notify };
}
setup 的返回值
setup 的返回值决定了模板可以访问的内容:
- 对象:返回的对象属性自动暴露给模板。
- 渲染函数(高级用法):直接返回
h函数生成的 VNode。
示例:返回渲染函数
<script>
import { h } from 'vue';
export default {
setup() {
return () => h('div', 'Hello from render function');
}
};
</script>
- 效果:页面显示“Hello from render function”。
在 setup 中使用生命周期钩子
Vue 3 将生命周期钩子整合到 Composition API 中,通过导入函数在 setup 中调用:
onBeforeMount、onMounted、onBeforeUpdate、onUpdated等。
示例:生命周期钩子
<template>
<p>{{ message }}</p>
</template>
<script>
import { ref, onMounted, onUnmounted } from 'vue';
export default {
setup() {
const message = ref('等待中...');
onMounted(() => {
message.value = '组件已挂载!';
console.log('Mounted');
});
onUnmounted(() => {
console.log('Unmounted');
});
return { message };
}
};
</script>
- 效果:组件挂载后显示“组件已挂载!”,销毁时打印日志。
生命周期对照
| Options API | Composition API |
|---|---|
beforeCreate | 无需(setup 替代) |
created | 无需(setup 替代) |
beforeMount | onBeforeMount |
mounted | onMounted |
setup 中的 this
与 Options API 不同,setup 中没有 this:
- 原因:
setup在组件实例创建之前运行,无法访问实例。 - 替代:通过
props和context获取所需数据。
示例:避免 this
<script>
export default {
methods: {
sayHi() {} // Options API 中可用
},
setup() {
// console.log(this.sayHi); // undefined
const sayHi = () => console.log('Hi from setup');
return { sayHi };
}
};
</script>
综合示例
结合 props、context 和生命周期钩子,实现一个计数器组件:
<template>
<div>
<p>{{ greeting }},计数:{{ count }}</p>
<button @click="increment">增加</button>
</div>
</template>
<script>
import { ref, onMounted, onUnmounted } from 'vue';
export default {
props: {
greeting: String
},
setup(props, { emit }) {
const count = ref(0);
const increment = () => {
count.value++;
emit('count-changed', count.value);
};
onMounted(() => {
console.log(`${props.greeting} 组件挂载`);
});
onUnmounted(() => {
console.log('组件销毁');
});
return { count, increment };
}
};
</script>
- 用法:
<Counter greeting="Hello" @count-changed="logCount" /> <script> export default { setup() { const logCount = (value) => console.log('计数:', value); return { logCount }; } }; </script> - 效果:显示问候语和计数器,点击增加并触发事件。
注意事项
- 单一职责:避免
setup函数过于庞大,必要时拆分为自定义函数。 - 响应式丢失:返回普通对象可能导致响应性丢失,确保使用
ref或reactive。const state = reactive({ count: 0 }); return { state }; // 正确 return { count: state.count }; // 错误,失去响应性 - 与 Options API 混合:两者可共存,但建议统一风格。
总结
setup 函数是 Composition API 的基石,统一了响应式数据、方法和生命周期的管理。它通过 props 和 context 提供外部输入和上下文支持,返回值灵活适配模板需求。掌握 setup 的用法后,你将能更高效地组织组件逻辑。下一节将探讨如何利用它实现高级功能,深入 Composition API 的世界!
