您好,登录后才能下订单哦!
# 前端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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。