## Vue 3 的 Setup 函数:一个通俗易懂的讲座
大家好!欢迎来到今天的 Vue 3 讲座。今天我们要聊聊 Vue 3 中一个非常核心的概念:`setup` 函数。它就像 Vue 3 组件的大脑,负责管理组件的状态、逻辑和生命周期。
### 什么是 Setup 函数?
简单来说,`setup` 函数是一个在组件创建*之前*执行的函数。它为我们提供了一个地方来:
* 声明响应式状态 (reactive state)
* 注册方法 (methods)
* 处理计算属性 (computed properties)
* 监听侦听器 (watchers)
* 访问生命周期钩子 (lifecycle hooks)
* 返回模板中需要使用的任何内容
可以把它想象成一个初始化函数,它决定了组件的一切。
### Setup 函数的执行时机
这非常重要!`setup` 函数在以下时机执行:
* **在 `beforeCreate` 生命周期钩子*之前***
* **在 `created` 生命周期钩子*之前***
这意味着,在 `setup` 函数中,你无法访问到 `this`,因为它还没有被创建。 `this` 在 Vue 3 的 Composition API 中基本被抛弃了,我们更推荐使用函数式的方式来组织代码。
### Setup 函数的作用
`setup` 函数是 Vue 3 Composition API 的核心,它负责:
1. **响应式状态管理**: 使用 `reactive` 和 `ref` 创建响应式数据。
2. **逻辑组织**: 将组件的逻辑组织成可复用的函数。
3. **生命周期管理**: 通过 `onMounted`、`onUpdated`、`onUnmounted` 等函数来管理组件的生命周期。
4. **模板数据暴露**: 返回一个对象,该对象中的属性和方法可以在组件的模板中使用。
### Setup 函数的基本结构
一个基本的 `setup` 函数看起来像这样:
```javascript
import { reactive, ref, onMounted } from 'vue';
export default {
setup() {
// 1. 声明响应式状态
const count = ref(0);
const message = reactive({
text: 'Hello Vue 3!'
});
// 2. 定义方法
const increment = () => {
count.value++;
};
// 3. 使用生命周期钩子
onMounted(() => {
console.log('Component mounted!');
});
// 4. 返回模板需要使用的内容
return {
count,
message,
increment
};
}
}
让我们分解一下:
import
语句: 从vue
模块导入需要的函数,比如reactive
、ref
、onMounted
。export default
: 导出组件选项对象。setup()
函数: 这是核心,所有的逻辑都在这里面。- 响应式状态: 使用
ref
和reactive
创建响应式数据。ref
用于基本类型,reactive
用于对象。 - 方法: 定义组件中使用的方法。
- 生命周期钩子: 使用
onMounted
等函数注册生命周期钩子。 return
语句: 返回一个对象,该对象中的属性和方法可以在模板中使用。
响应式状态:Ref vs Reactive
这是 setup
函数中最关键的部分之一。Vue 3 提供了两种创建响应式数据的方式:ref
和 reactive
。
ref
: 用于包装基本类型的数据,比如数字、字符串、布尔值。你需要使用.value
来访问或修改ref
的值。reactive
: 用于包装对象。你可以直接访问和修改对象的属性。
下面是一个例子:
import { ref, reactive } from 'vue';
export default {
setup() {
const count = ref(0); // count 是一个 ref 对象
const person = reactive({ // person 是一个 reactive 对象
name: 'Alice',
age: 30
});
const increment = () => {
count.value++; // 使用 .value 访问 ref 的值
};
const changeName = (newName) => {
person.name = newName; // 直接修改 reactive 对象的属性
};
return {
count,
person,
increment,
changeName
};
},
template: `
<p>Count: {{ count }}</p>
<p>Name: {{ person.name }}, Age: {{ person.age }}</p>
<button @click="increment">Increment</button>
<button @click="changeName('Bob')">Change Name</button>
`
};
生命周期钩子:Composition API 的方式
在 Vue 3 的 Composition API 中,我们不再使用 beforeCreate
、created
等选项式的生命周期钩子。而是使用 onMounted
、onUpdated
、onUnmounted
等函数来注册生命周期钩子。
这些函数都需要在 setup
函数中调用。
Options API | Composition API | 说明 |
---|---|---|
beforeCreate |
N/A | 在 setup 函数中执行的代码相当于 beforeCreate 和 created 。因为 setup 函数在组件实例创建之前执行。 |
created |
N/A | 同上。 |
beforeMount |
onBeforeMount |
在组件挂载到 DOM 之前调用。 |
mounted |
onMounted |
在组件挂载到 DOM 之后调用。 |
beforeUpdate |
onBeforeUpdate |
在组件更新之前调用。 |
updated |
onUpdated |
在组件更新之后调用。 |
beforeUnmount |
onBeforeUnmount |
在组件卸载之前调用。 |
unmounted |
onUnmounted |
在组件卸载之后调用。 |
errorCaptured |
onErrorCaptured |
当捕获到一个来自子组件的错误时被调用。 |
renderTracked |
onRenderTracked |
在渲染函数被追踪时调用。 |
renderTriggered |
onRenderTriggered |
在渲染函数被触发时调用。 |
activated |
onActivated |
在 keep-alive 组件激活时调用。 |
deactivated |
onDeactivated |
在 keep-alive 组件停用时调用。 |
serverPrefetch |
onServerPrefetch |
在服务器端渲染期间执行预取。 |
例如:
import { onMounted, onUpdated, onUnmounted } from 'vue';
export default {
setup() {
onMounted(() => {
console.log('Component mounted!');
});
onUpdated(() => {
console.log('Component updated!');
});
onUnmounted(() => {
console.log('Component unmounted!');
});
return {}; // 必须返回一个对象,即使是空对象
}
}
计算属性和侦听器
setup
函数也负责处理计算属性和侦听器。
- 计算属性 (Computed Properties): 使用
computed
函数创建。 - 侦听器 (Watchers): 使用
watch
函数创建。
import { ref, computed, watch } from 'vue';
export default {
setup() {
const count = ref(0);
// 计算属性
const doubleCount = computed(() => {
return count.value * 2;
});
// 侦听器
watch(
count,
(newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`);
}
);
const increment = () => {
count.value++;
};
return {
count,
doubleCount,
increment
};
},
template: `
<p>Count: {{ count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
`
};
Setup 函数的返回值
setup
函数必须返回一个对象。这个对象包含你希望在模板中使用的所有属性和方法。
- 响应式状态:
ref
和reactive
对象。 - 方法: 组件中使用的方法。
- 计算属性:
computed
对象。
如果你不需要在模板中使用任何东西,仍然需要返回一个空对象:
export default {
setup() {
// 一些逻辑...
return {}; // 返回一个空对象
}
}
Setup 函数与 Options API 的对比
Vue 2 使用 Options API,而 Vue 3 引入了 Composition API。 setup
函数是 Composition API 的核心。
特性 | Options API | Composition API (Setup 函数) |
---|---|---|
组织代码 | 基于选项 (data, methods, computed, watch 等) | 基于逻辑功能 (functions) |
代码复用 | Mixins | Composables (函数) |
this 上下文 |
可访问组件实例 (this) | 没有 this ,需要显式地导入和使用所需的功能。 |
类型推断 | 较弱 | 更好,尤其是在 TypeScript 中 |
心智负担 | 初学者友好,但大型组件可能难以维护。 | 学习曲线较陡峭,但更灵活,更易于维护和测试。 |
为什么使用 Setup 函数?
- 更好的代码组织: 可以将相关的逻辑组织在一起,提高代码的可读性和可维护性。
- 更好的代码复用: 可以将
setup
函数中的逻辑提取到单独的函数中,方便在不同的组件中复用。这些可复用的函数通常被称为 "Composables"。 - 更好的类型推断: 在 TypeScript 中,
setup
函数可以提供更好的类型推断,减少错误。 - 更灵活: 提供了更大的灵活性,可以更自由地组织代码。
- 减少
this
的使用: 避免了this
上下文的混淆,使代码更易于理解和测试。
一个更复杂的例子:使用 API 获取数据
import { ref, onMounted } from 'vue';
export default {
setup() {
const posts = ref([]);
const loading = ref(false);
const error = ref(null);
const fetchPosts = async () => {
loading.value = true;
error.value = null;
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!response.ok) {
throw new Error('Failed to fetch posts');
}
posts.value = await response.json();
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
};
onMounted(() => {
fetchPosts();
});
return {
posts,
loading,
error
};
},
template: `
<div v-if="loading">Loading...</div>
<div v-if="error">Error: {{ error }}</div>
<ul v-else>
<li v-for="post in posts" :key="post.id">
<h3>{{ post.title }}</h3>
<p>{{ post.body }}</p>
</li>
</ul>
`
};
在这个例子中,我们:
- 使用
ref
创建了posts
、loading
和error
三个响应式状态。 - 定义了一个
fetchPosts
函数,用于从 API 获取数据。 - 使用
onMounted
生命周期钩子在组件挂载后调用fetchPosts
函数。 - 返回了
posts
、loading
和error
,以便在模板中使用。
避免的坑
- 不要在
setup
函数中使用this
: 因为this
在setup
函数中是undefined
。 - 确保
setup
函数返回一个对象: 即使是空对象也要返回。 - 在
setup
函数中注册生命周期钩子: 使用onMounted
、onUpdated
、onUnmounted
等函数。 - 理解
ref
和reactive
的区别:ref
用于基本类型,reactive
用于对象。 - 小心闭包: 在
setup
函数中定义的变量可能会被闭包捕获,导致一些意想不到的结果。
总结
setup
函数是 Vue 3 Composition API 的核心。它为我们提供了一个地方来声明响应式状态、注册方法、处理计算属性、监听侦听器和访问生命周期钩子。通过 setup
函数,我们可以更好地组织代码、提高代码的可读性和可维护性,以及更好地复用代码。
希望今天的讲座能够帮助你更好地理解 Vue 3 的 setup
函数。 实践是最好的老师, 动手写代码,你会发现 setup
函数的强大之处。
谢谢大家!