您好,登录后才能下订单哦!
# 前端Vue3的setup如何使用
## 一、setup函数概述
### 1.1 setup函数的诞生背景
Vue 3.0作为新一代前端框架,引入了Composition API这一革命性特性。setup函数正是Composition API的核心入口,它的出现主要解决了以下问题:
1. **逻辑关注点分离**:Options API在复杂组件中会导致相同逻辑分散在不同选项中
2. **更好的类型推断**:对TypeScript支持更加友好
3. **逻辑复用能力**:通过组合函数实现更好的逻辑复用
4. **更灵活的代码组织**:可以按逻辑而非选项类型组织代码
### 1.2 setup函数的基本特性
```javascript
import { ref, reactive } from 'vue'
export default {
  setup(props, context) {
    // 响应式状态
    const count = ref(0)
    const state = reactive({ name: 'Vue' })
    
    // 方法
    function increment() {
      count.value++
    }
    
    // 生命周期钩子
    onMounted(() => {
      console.log('组件已挂载')
    })
    
    // 返回模板使用的数据和方法
    return {
      count,
      state,
      increment
    }
  }
}
setup函数具有以下关键特点:
- 在组件创建之前执行(在beforeCreate之前)
- 接收props和context两个参数
- 返回的对象将暴露给模板和组件实例
- 内部可以访问组件的生命周期钩子
export default {
  props: {
    title: String,
    user: {
      type: Object,
      required: true
    }
  },
  setup(props) {
    console.log(props.title)
    // 注意:props是响应式的,但不要解构它!
    const { user } = toRefs(props)
    return { user }
  }
}
注意事项:
- props是响应式的,但不要直接解构(会失去响应性)
- 使用toRefs转换后再解构
- props在子组件中不可修改(保持单向数据流)
setup(props, context) {
  // 属性(非响应式对象)
  console.log(context.attrs)
  
  // 插槽(非响应式对象)
  console.log(context.slots)
  
  // 触发事件(方法)
  console.log(context.emit)
  
  // 暴露公共属性(方法)
  console.log(context.expose)
}
context对象包含:
- attrs:未在props中声明的属性
- slots:父组件传入的插槽内容
- emit:触发自定义事件的方法
- expose:暴露公共属性的方法
import { ref, reactive } from 'vue'
setup() {
  // 基本类型使用ref
  const count = ref(0)
  
  // 引用类型可使用reactive
  const state = reactive({
    name: 'Vue 3',
    version: '3.2.0'
  })
  
  // 修改值
  count.value = 1
  state.name = 'Vue.js'
  
  return { count, state }
}
对比说明:
| 特性 | ref | reactive | 
|---|---|---|
| 适用类型 | 基本/引用类型 | 仅引用类型 | 
| 访问方式 | 通过.value | 直接访问 | 
| 解构响应性 | 保持 | 丢失 | 
| 模板中使用 | 自动解包.value | 直接访问 | 
import { ref, computed } from 'vue'
setup() {
  const firstName = ref('张')
  const lastName = ref('三')
  
  // 计算属性
  const fullName = computed(() => {
    return `${firstName.value}${lastName.value}`
  })
  
  // 可写的计算属性
  const writableFullName = computed({
    get: () => `${firstName.value}${lastName.value}`,
    set: (newValue) => {
      [firstName.value, lastName.value] = newValue.split(' ')
    }
  })
  
  return { fullName, writableFullName }
}
import { ref, watch, watchEffect } from 'vue'
setup() {
  const count = ref(0)
  const state = reactive({ name: 'Vue' })
  
  // 基本watch用法
  watch(count, (newVal, oldVal) => {
    console.log(`count变化: ${oldVal} -> ${newVal}`)
  })
  
  // 监听多个源
  watch([count, () => state.name], ([newCount, newName]) => {
    console.log(`count或name变化`, newCount, newName)
  }, { deep: true })
  
  // watchEffect自动追踪依赖
  watchEffect(() => {
    console.log('count或name变化:', count.value, state.name)
  })
  
  return { count, state }
}
| Options API | Composition API | 
|---|---|
| beforeCreate | 不需要(setup替代) | 
| created | 不需要(setup替代) | 
| beforeMount | onBeforeMount | 
| mounted | onMounted | 
| beforeUpdate | onBeforeUpdate | 
| updated | onUpdated | 
| beforeUnmount | onBeforeUnmount | 
| unmounted | onUnmounted | 
| errorCaptured | onErrorCaptured | 
| renderTracked | onRenderTracked | 
| renderTriggered | onRenderTriggered | 
import { onMounted, onUpdated, onUnmounted } from 'vue'
setup() {
  onMounted(() => {
    console.log('组件挂载完成')
    // 执行DOM操作或API请求
  })
  
  onUpdated(() => {
    console.log('组件已更新')
  })
  
  onUnmounted(() => {
    console.log('组件即将卸载')
    // 清理定时器、取消订阅等
  })
}
<template>
  <input ref="inputRef" />
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
  setup() {
    const inputRef = ref(null)
    
    onMounted(() => {
      inputRef.value.focus()
    })
    
    return { inputRef }
  }
}
</script>
// 子组件 Child.vue
export default {
  setup(props, { emit }) {
    const sendMessage = () => {
      emit('message', 'Hello from child!')
    }
    
    return { sendMessage }
  }
}
// 父组件 Parent.vue
<template>
  <Child @message="handleMessage" />
</template>
<script>
export default {
  setup() {
    const handleMessage = (msg) => {
      console.log(msg) // 'Hello from child!'
    }
    
    return { handleMessage }
  }
}
</script>
// useCounter.js
import { ref } from 'vue'
export function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  
  function increment() {
    count.value++
  }
  
  function decrement() {
    count.value--
  }
  
  function reset() {
    count.value = initialValue
  }
  
  return { count, increment, decrement, reset }
}
// 组件中使用
import { useCounter } from './useCounter'
export default {
  setup() {
    const { count, increment } = useCounter(10)
    return { count, increment }
  }
}
import { defineComponent, ref } from 'vue'
interface User {
  name: string
  age: number
}
export default defineComponent({
  props: {
    message: {
      type: String,
      required: true
    }
  },
  setup(props) {
    const count = ref<number>(0)
    const user = ref<User>({ name: 'Alice', age: 25 })
    
    return { count, user }
  }
})
// 错误做法
setup() {
  const state = reactive({ count: 0 })
  return {
    ...state // 响应性丢失!
  }
}
// 正确做法1
setup() {
  const state = reactive({ count: 0 })
  return {
    state // 保持响应性
  }
}
// 正确做法2
import { toRefs } from 'vue'
setup() {
  const state = reactive({ count: 0 })
  return {
    ...toRefs(state) // 转换为ref
  }
}
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
  import('./components/AsyncComponent.vue')
)
// 在setup中使用
setup() {
  return {
    AsyncComp
  }
}
import { useStore } from 'vuex'
export default {
  setup() {
    const store = useStore()
    
    const count = computed(() => store.state.count)
    
    function increment() {
      store.commit('increment')
    }
    
    return { count, increment }
  }
}
Vue 3的setup函数为组件开发带来了全新的编程范式,通过本文我们全面了解了:
随着Vue 3生态的不断完善,setup语法糖(<script setup>)进一步简化了使用方式,但底层原理与本文介绍的setup函数完全一致。掌握好这些基础知识,将帮助您更好地适应Vue 3的开发模式,构建更健壮、更易维护的前端应用。
未来,Composition API还将继续演进,与Vue的响应式系统、编译器优化等特性深度整合,为开发者带来更出色的开发体验。建议持续关注Vue官方文档和RFC提案,及时了解最新进展。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。