Vue3:setup详解

Vue3:setup详解

  • [一、setup 是什么?](#一、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 中的 beforeCreatecreated 钩子。

复制代码
执行顺序:
setup() → beforeCreate() → created() → ...其他生命周期

⚠️ 关键点:setup 中 thisundefined,不能用 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 中 thisundefined
✅ 返回值暴露给模板 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 更灵活、更好复用!

相关推荐
Patrick_Wilson2 小时前
修好 bug 只是开始:一次由监控需求反向重构日志结构的复盘
前端·监控·数据可视化
kyriewen2 小时前
面试官让我优化一个卡死的表格,我打开 Claude 五秒重写,他放下咖啡问我要不要来当组长
前端·javascript·面试
小粉粉hhh3 小时前
React(二)——dom、组件间通信、useEffect
前端·javascript·react.js
Csvn3 小时前
🤖 AI 生成前端代码的 5 个典型翻车现场及修复指南
前端
IT_陈寒4 小时前
SpringBoot自动装配坑了我一天,原来问题出在这
前端·人工智能·后端
索西引擎5 小时前
【React】key 属性:协调算法中的元素标识机制与最佳实践
前端·javascript·react.js
只会cv的前端攻城狮5 小时前
动态组件架构设计:从配置到事件驱动全链路解析
前端·vue.js
飞天狗5 小时前
Next.js 16 App Router 实战:服务端组件与流式渲染
前端·next.js
万敏5 小时前
Vue3 全栈实战第二周:Pinia + Vite + Router + axios 完整记录
vue.js·node.js·全栈