Vue3:setup详解
- [一、setup 是什么?](#一、setup 是什么?)
- 二、基本语法
-
- 写法一:选项式写法(传统)
- [写法二:`<script setup>` 语法糖(推荐 ⭐⭐⭐⭐⭐)](#写法二:
<script setup>语法糖(推荐 ⭐⭐⭐⭐⭐))
- [三、setup 的两个参数](#三、setup 的两个参数)
-
- [3.1 props --- 父组件传参](#3.1 props — 父组件传参)
- [3.2 context --- 上下文](#3.2 context — 上下文)
- [四、setup 能做什么?](#四、setup 能做什么?)
-
- [4.1 声明响应式数据](#4.1 声明响应式数据)
- [4.2 计算属性 computed](#4.2 计算属性 computed)
- [4.3 侦听器 watch](#4.3 侦听器 watch)
- [4.4 生命周期钩子](#4.4 生命周期钩子)
- [4.5 方法定义](#4.5 方法定义)
- [五、逻辑复用(组合函数 useXxx)](#五、逻辑复用(组合函数 useXxx))
- [六、setup 的注意事项](#六、setup 的注意事项)
- 七、完整示例
一、setup 是什么?
setup 是 Vue 3 引入的组合式 API(Composition API)核心入口,是一个在组件实例创建之前执行的函数,用于替代 Vue 2 中的 beforeCreate 和 created 钩子。
执行顺序:
setup() → beforeCreate() → created() → ...其他生命周期
⚠️ 关键点:setup 中
this是undefined,不能用this访问组件实例!
二、基本语法
写法一:选项式写法(传统)
javascript
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">+1</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
// ✅ Vue3 新增选项
setup() {
const count = ref(0)
const increment = () => { count.value++ }
// 🎯 返回值暴露给模板
return { count, increment }
}
}
</script>
写法二:<script setup> 语法糖(推荐 ⭐⭐⭐⭐⭐)
javascript
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">+1</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
// ✅ 不需要 return,自动暴露给模板
const count = ref(0)
const increment = () => { count.value++ }
</script>
💡 推荐用
<script setup>,更简洁,不用写return!
三、setup 的两个参数
js
setup(props, context) {
// props → 父组件传来的数据(响应式)
// context → 上下文对象 { attrs, slots, emit }
}
3.1 props --- 父组件传参
js
export default {
props: {
title: String,
count: { type: Number, default: 0 }
},
setup(props) {
console.log(props.title) // ✅ 直接访问
console.log(props.count)
// ⚠️ 不要解构!会丢失响应性
// const { title } = props ❌
// ✅ 要解构用 toRefs
import { toRefs } from 'vue'
const { title } = toRefs(props) // title.value
}
}
3.2 context --- 上下文
js
setup(props, { attrs, slots, emit }) {
// attrs → 未在 props 中声明的属性
// slots → 插槽内容
// emit → 触发父组件事件
const sendEvent = () => {
emit('custom-event', 'hello') // 通知父组件
}
return { sendEvent }
}
四、setup 能做什么?
4.1 声明响应式数据
| API | 用途 | 示例 |
|---|---|---|
ref() |
单个值(基本类型/对象) | const count = ref(0) |
reactive() |
对象/数组 | const state = reactive({ name: 'Tom' }) |
js
import { ref, reactive } from 'vue'
setup() {
// ref:JS 中用 .value,模板中自动解包不用 .value
const count = ref(0)
// reactive:直接访问属性
const user = reactive({
name: '张三',
age: 20
})
return { count, user }
}
4.2 计算属性 computed
js
import { ref, computed } from 'vue'
setup() {
const count = ref(0)
const double = computed(() => count.value * 2)
return { count, double }
}
4.3 侦听器 watch
js
import { ref, watch } from 'vue'
setup() {
const count = ref(0)
watch(count, (newVal, oldVal) => {
console.log(`从 ${oldVal} 变为 ${newVal}`)
})
return { count }
}
4.4 生命周期钩子
js
import {
onMounted,
onUpdated,
onUnmounted,
onBeforeMount,
onBeforeUpdate
} from 'vue'
setup() {
onMounted(() => {
console.log('组件挂载完成')
})
onUnmounted(() => {
console.log('组件销毁')
// 清除定时器等操作
})
return {}
}
4.5 方法定义
js
setup() {
const count = ref(0)
// 普通函数
const increment = () => { count.value++ }
// 或返回响应式方法
const reset = () => { count.value = 0 }
return { count, increment, reset }
}
五、逻辑复用(组合函数 useXxx)
这是 setup 最大的优势!把逻辑抽成独立函数:
js
// useCounter.js
import { ref } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const increment = () => count.value++
const decrement = () => count.value--
return { count, increment, decrement }
}
javascript
<!-- 组件中使用 -->
<script setup>
import { useCounter } from './useCounter'
const { count, increment, decrement } = useCounter(10)
</script>
<template>
<p>{{ count }}</p>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
</template>
🎯 多个组件可以复用同一个
useXxx,代码不重复!
六、setup 的注意事项
| 规则 | 说明 |
|---|---|
❌ 不能用 this |
setup 中 this 是 undefined |
| ✅ 返回值暴露给模板 | return { xxx } 或 <script setup> 自动暴露 |
| ⚠️ 不要解构 props | 会丢失响应性,用 toRefs() |
| ⚠️ 不要解构 context.attrs/slots | 它们是非响应式的 |
| 🔄 异步支持 | 可以返回 Promise,配合 <Suspense> 使用 |
| 📦 Vue2 选项可访问 setup | 但 setup 不能访问 Vue2 的 data/methods |
七、完整示例
javascript
<template>
<div class="user-card">
<h2>{{ user.name }} - {{ user.age }}岁</h2>
<p>双倍年龄:{{ doubleAge }}</p>
<button @click="increment">年龄+1</button>
<button @click="changeName">改名</button>
<slot></slot>
</div>
</template>
<script setup>
import { ref, reactive, computed, onMounted, watch } from 'vue'
// ✅ 接收 props
const props = defineProps({
initialName: { type: String, default: '未知' }
})
// ✅ 响应式数据
const user = reactive({
name: props.initialName,
age: 18
})
// ✅ 计算属性
const doubleAge = computed(() => user.age * 2)
// ✅ 方法
const increment = () => { user.age++ }
const changeName = () => { user.name = '新名字' }
// ✅ 侦听
watch(() => user.age, (newAge) => {
console.log(`年龄变为 ${newAge}`)
})
// ✅ 生命周期
onMounted(() => {
console.log('组件挂载了!')
})
// ✅ 触发事件(给父组件)
const emit = defineEmits(['age-changed'])
</script>
<style scoped>
.user-card {
padding: 20px;
border: 1px solid ccc;
border-radius: 8px;
}
</style>
📋 一图总结
setup()
├── 📥 参数:props(父传子数据)+ context(attrs/slots/emit)
├── 📦 声明:ref / reactive(响应式数据)
├── 🧮 计算:computed(派生值)
├── 👀 侦听:watch(数据变化)
├── 🔄 生命周期:onMounted / onUnmounted 等
├── 🛠️ 方法:普通函数 / 箭头函数
├── 📤 返回:暴露给模板使用
└── 🔁 复用:抽成 useXxx 组合函数
💡 总结一句话:setup 就是 Vue 3 的"大脑",把组件的数据、方法、生命周期全部组织在一个函数里,比 Vue 2 更灵活、更好复用!