大家好,我是老码农,今天咱们来聊聊Vue 3 Composition API里的“扛把子”—— setup
函数!
别被“深度解析”吓到,咱们的目标是用最接地气的方式,把setup
函数扒个精光,让大家以后用起来得心应手。
开场白:为什么要有 setup
?
在Vue 2时代,咱们写组件,数据、方法、生命周期钩子一股脑儿全塞进 data
、methods
、mounted
这些选项里。组件大了,代码就容易乱成一锅粥,逻辑复用也比较麻烦。
Vue 3 的 Composition API 就像一剂良药,它允许我们把组件的逻辑按功能模块组织起来,每个模块就是个“composable function”(组合式函数)。而 setup
函数,就是这些 “composable functions” 的舞台,我们可以在里面定义响应式数据、方法,并且把它们return出去,供模板使用。
setup
函数:组件的“初始化司令部”
setup
函数是 Vue 3 组件中一个全新的选项,它在组件实例创建之前执行,可以把它看作是组件的“初始化司令部”。
特点:
- 只执行一次: 组件每次创建实例,
setup
都只会执行一次。 - 访问不到
this
: 因为在setup
执行时,组件实例还没创建呢,所以访问不到this
。 - 必须返回值:
setup
函数必须返回一个对象,里面的属性和方法会被合并到组件实例的渲染上下文中,这样模板才能访问到。 当然也可以不返回。
基本结构:
import { ref, reactive, onMounted } from 'vue';
export default {
setup() {
// 1. 定义响应式数据
const count = ref(0);
const state = reactive({
message: 'Hello Vue 3!'
});
// 2. 定义方法
const increment = () => {
count.value++; // 注意:ref的值需要通过 .value 访问
};
// 3. 注册生命周期钩子
onMounted(() => {
console.log('Component mounted!');
});
// 4. 返回值 (必须是一个对象)
return {
count,
state,
increment
};
},
template: `
<div>
<p>{{ state.message }}</p>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
`
};
setup
函数的参数:props
和 context
setup
函数可以接收两个参数:
props
: 一个响应式对象,包含父组件传递给当前组件的所有 props。context
: 一个对象,暴露了组件的一些内部属性和方法。
1. props
props
类似于 Vue 2 中的 this.$props
,但它是响应式的。如果父组件传递的 props 发生了变化,setup
函数中的 props
对象也会自动更新。
例子:
export default {
props: {
name: {
type: String,
required: true
}
},
setup(props) {
console.log('Props:', props.name); // 访问 props
// 使用 watch 监听 props 的变化
watch(() => props.name, (newValue, oldValue) => {
console.log('Name changed from', oldValue, 'to', newValue);
});
return {}; // 必须返回一个对象
},
template: `<div>Hello, {{ name }}!</div>`
};
注意点:
- 不要在
setup
函数中解构props
对象,因为那样会失去响应性。- 错误示范:
const { name } = props;
这样name
就不是响应式的了。
- 错误示范:
- 如果需要解构
props
,可以使用toRefs
函数。
import { toRefs } from 'vue';
export default {
props: {
name: {
type: String,
required: true
},
age: {
type: Number,
default: 18
}
},
setup(props) {
const { name, age } = toRefs(props); // 使用 toRefs 解构 props
console.log('Name:', name.value); // 访问 ref 的值
// 使用 watch 监听 props 的变化
watch(name, (newValue, oldValue) => {
console.log('Name changed from', oldValue, 'to', newValue);
});
return {
name,
age
};
},
template: `<div>Hello, {{ name }}! Age: {{age}}</div>`
};
2. context
context
对象提供了一些有用的属性和方法:
属性/方法 | 说明 |
---|---|
attrs |
一个非响应式的对象,包含组件的所有 attribute (除了 class 和 style )。 相当于 Vue 2 的 $attrs 。 |
emit |
一个函数,用于触发自定义事件。相当于 Vue 2 的 $emit 。 |
slots |
一个对象,包含组件的所有插槽。相当于 Vue 2 的 $slots 。 |
expose |
一个函数,用于显式地暴露组件的属性和方法给父组件。 |
例子:
export default {
setup(props, context) {
const { emit, attrs, slots, expose } = context;
// 触发自定义事件
const handleClick = () => {
emit('my-event', 'Hello from child component!');
};
// 访问 attrs
console.log('Attrs:', attrs);
// 访问 slots
console.log('Slots:', slots);
//暴露方法
const childMethod = () => {
console.log("child method called!")
}
expose({
childMethod
})
return {
handleClick
};
},
template: `
<div>
<button @click="handleClick">Click me</button>
<slot></slot>
</div>
`
};
父组件:
<template>
<div>
<ChildComponent @my-event="handleMyEvent" message="Parent message">
<template #default>
This is a slot content from parent.
</template>
</ChildComponent>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
handleMyEvent(message) {
console.log('Received event:', message);
}
},
mounted(){
this.$refs.childComponent.childMethod()
}
};
</script>
注意点:
attrs
是非响应式的,如果需要监听 attribute 的变化,可以使用watch
。slots
是只读的,不能在setup
函数中修改插槽内容。
setup
函数的执行顺序与生命周期
setup
函数的执行时机非常重要,它直接影响着我们对组件生命周期的理解。
执行顺序:
beforeCreate
: 在组件实例被创建之前执行。 Vue2 的生命周期setup
: 在beforeCreate
之后,created
之前执行。这是 Composition API 的入口。created
: 在组件实例创建完成后执行。 Vue2 的生命周期beforeMount
: 在组件挂载到 DOM 之前执行。onBeforeMount
: Composition API 提供的钩子,等同于beforeMount
。mounted
: 在组件挂载到 DOM 后执行。onMounted
: Composition API 提供的钩子,等同于mounted
。beforeUpdate
: 在组件更新之前执行。onBeforeUpdate
: Composition API 提供的钩子,等同于beforeUpdate
。updated
: 在组件更新之后执行。onUpdated
: Composition API 提供的钩子,等同于updated
。beforeUnmount
: 在组件卸载之前执行。onBeforeUnmount
: Composition API 提供的钩子,等同于beforeUnmount
。unmounted
: 在组件卸载之后执行。onUnmounted
: Composition API 提供的钩子,等同于unmounted
。errorCaptured
和onErrorCaptured
:当捕获一个来自后代组件的错误时被调用。Composition API 提供了onErrorCaptured
钩子。onRenderTracked
和onRenderTriggered
:这两个钩子是用于调试的。Composition API 提供了对应的onRenderTracked
和onRenderTriggered
钩子。deactivated
和activated
: 这两个生命周期钩子是专门用于<keep-alive>
组件的。当一个组件被keep-alive
缓存时,deactivated
会在组件被移除时调用,activated
会在组件被重新插入时调用。Vue 3 的 Composition API 中,我们可以使用onDeactivated
和onActivated
钩子来注册相应的回调函数。
生命周期钩子:
Vue 3 的 Composition API 提供了与 Vue 2 类似的生命周期钩子,但它们的使用方式略有不同。
Vue 2 选项 | Composition API 钩子 | 说明 |
---|---|---|
beforeCreate |
N/A | setup 函数替代了 beforeCreate 和 created 的作用。 |
created |
N/A | setup 函数替代了 beforeCreate 和 created 的作用。 |
beforeMount |
onBeforeMount |
在组件挂载到 DOM 之前执行。 |
mounted |
onMounted |
在组件挂载到 DOM 后执行。 |
beforeUpdate |
onBeforeUpdate |
在组件更新之前执行。 |
updated |
onUpdated |
在组件更新之后执行。 |
beforeUnmount |
onBeforeUnmount |
在组件卸载之前执行。 |
unmounted |
onUnmounted |
在组件卸载之后执行。 |
errorCaptured |
onErrorCaptured |
当捕获一个来自后代组件的错误时被调用。 |
例子:
import { onMounted, onUpdated, onUnmounted } from 'vue';
export default {
setup() {
console.log('setup() is running');
onMounted(() => {
console.log('Component mounted!');
});
onUpdated(() => {
console.log('Component updated!');
});
onUnmounted(() => {
console.log('Component unmounted!');
});
return {};
},
template: `<div>Hello, Vue 3!</div>`
};
注意点:
- Composition API 的生命周期钩子需要在
setup
函数中注册。 - 不要在 Composition API 的生命周期钩子中使用
this
。
案例分析:一个简单的计数器组件
咱们来用 setup
函数实现一个简单的计数器组件,加深理解。
import { ref, onMounted, onUnmounted } from 'vue';
export default {
setup() {
// 1. 定义响应式数据
const count = ref(0);
// 2. 定义方法
const increment = () => {
count.value++;
};
const decrement = () => {
count.value--;
};
// 3. 注册生命周期钩子
onMounted(() => {
console.log('Counter component mounted!');
// 可以在这里执行一些初始化操作
});
onUnmounted(() => {
console.log('Counter component unmounted!');
// 可以在这里清理一些资源
});
// 4. 返回值
return {
count,
increment,
decrement
};
},
template: `
<div>
<p>Count: {{ count }}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
`
};
总结:setup
函数的价值
- 更好的代码组织: 让我们能够按功能模块组织代码,提高代码的可读性和可维护性。
- 逻辑复用: 我们可以把通用的逻辑抽离成 composable functions,在不同的组件中复用。
- 更好的类型推断: TypeScript 对 Composition API 的支持更好,可以提供更准确的类型推断。
setup
函数是 Vue 3 Composition API 的核心,掌握它,就能更好地利用 Composition API 的优势,编写更优雅、更高效的 Vue 组件。
好了,今天的分享就到这里,希望对大家有所帮助。下次再见!