Vue3组合式API:从踩坑到高效实战全攻略

先看setup到底该怎么用?

很多教程一上来就讲setup里写代码,但没人告诉你:setup执行时机比created还早。所以别指望在setup里访问this。

我第一次写的时候,直接在setup里写this.$emit,然后控制台给我报了个“undefined”。气得我差点删库跑路。

正确的姿势是这样的:

javascript
// 为什么要这么写?因为setup根本没法用this
import { ref, onMounted } from 'vue'

export default {
setup() {
const message = ref('Hello Vue3!')

// 用onMounted替代created
onMounted((

) => {<

br /> console.log('组件挂载了,message:', message.value)
})

// 事件处理要返回方法
const handleClick = () => {
message.value = 'Clicked!'
}

return { message, handleClick }
}
}
`

刚接触这个设计,我真的觉得有点反人类。后来发现它其实让你更明确数据的来源和生命周期。记住:setup就是初始化所有逻辑的地方,不要在里面搞异步操作,除非你用await。

另一个坑:ref和reactive,到

选哪个?

刚开始我混着用,结果数据改了视图不更新,半天没找到原因。后来才发现,ref适合基本类型,reactive适合对象

但别以为reactive就万能——它不能直接替换整个对象!

看这段代码:

`javascript
// 为什么要这么写?reactive替换会丢失响应
import { reactive, ref } from 'vue'

// ❌ 错误做法
const user = reactive({ name: '张三', age: 18 })
user = { name: '李四', age: 20 } // 这样写视图不会更新!

// ✅ 正确做法
const user = ref({ name: '张三', age: 18 })
user.value = { name: '李四', age: 20 } // ref可以直接替换整个值

// ✅ 或者用reactive但只改属性
const user2 = reactive({ name: '张三', age: 18 })
user2.name = '李四' // 这样没问题
`

官方文档这段文档不够清晰,我就直说了:如果数据需要整体替换,用ref;如果只是改属性,用reactive。还有个技巧,ref内部其实也是用reactive实现的,所以ref更通用。我个人更倾向于默认用ref,真的省心。

实战:一个搜索组件带你飞

理论说再多不如写个实际案例。假设我们要做一个带防抖的搜索组件,用户输入后延迟300ms请求数据。用组合式API怎么写?

`javascript
// search.js - 逻辑复用模块
import { ref, watch, onUnmounted } from 'vue'

export function useSearch() {
const keyword = ref('') // 搜索关键词
const results = ref([]) // 搜索结果
const loading = ref(false) // 加载状态
let debounceTimer = null // 防抖定时器

const search = async (query) => {
if (!query.trim()) {
results.value = []
return
}

loading.value = true
try {
// 模拟API请求
const response = await fetch(
https://api.example.com/search?q=${query})
const data = await response.json()
results.value = data
} catch (error) {
console.error('搜索失败:', error)
results.value = []
} finally {
loading.value = false
}
}

// 监听keyword变化,实现防抖
watch(keyword, (newVal) => {
clearTimeout(debounceTimer)
debounceTimer = setTimeout(() => {
search(newVal)
}, 300)
})

// 清理定时器
onUnmounted(() => {
clearTimeout(debounceTimer)
})

return { keyword, results, loading }
}

// 在组件中使用
// <script setup>
// import { useSearch } from './search'
// const { keyword, results, loading } = useSearch()
// </script>
`

你看,这样一来,逻辑就完全独立了,哪个组件想用直接拿来用就行。之前Options API得靠mixins,数据来源都搞不清楚,现在useSearch一调用,所有变量清清楚楚。

对了,当时我写这个防抖的时候,一开始搞错了,忘了清理定时器,结果页面切换后还在发请求,又踩了一个坑。所以onUnmounted里的清理千万别漏掉。

还有个技巧:props的类型与默认值

组合式API里怎么处理props?很多人直接在setup的参数里写,但类型安全咋整?用defineProps配合TypeScript,但如果你不用TS,也有办法:

`javascript
// 为什么要这么写?确保props类型明确
import { defineComponent } from 'vue'

export default defineComponent({
props: {
title: {
type: String,
default: '默认标题'
},
count: {
type: Number,
required: true
}
},
setup(props) {
// props是响应式的,但别直接改它!
console.log(props.title, props.count)

// 如果需要修改,用计算属性
const displayCount = computed(() => props.count * 2)

return { displayCount }
}
})
`

其实这么设计挺有道理的:props只读,防止组件内部随意修改父组件数据。但如果你非想改,就用emit通知父组件。别学我当初直接props.title = ‘新标题’,然后找了一天bug。

数据对比:性能提升多少?

我用组合式API重构了一个旧项目,之前用Options API的列表渲染,每次数据更新都卡顿。重构后,通过ref精确控制响应式粒度,性能提升很明显。

具体数据是这样的:

  • 页面初始化时间:从3秒多降到了不到1秒
  • 内存占用:从45MB降到28MB
  • 代码行数:从1200行精简到800行

当然,这也跟项目本身有关,但组合式API的精确控制确实功不可没。

总结一下,你可以立刻用的三个点

  • 记住setup的执行时机:别在setup里用this,用onMounted替代created,用watch替代watch(虽然名字一样但用法不同)。
  • ref vs reactive选择原则:需要整体替换用ref,只改属性用reactive;如果拿不准,直接上ref,它最通用。真的好用。
  • 逻辑复用用组合式函数:把功能代码封装成useXxx`函数,让组件只做UI展示,逻辑全解耦。
  • 最后再啰嗦一句:别一上来就想着把所有逻辑都塞进setup里,保持一个函数只做一件事,代码读起来才爽。我现在写Vue3,再也不想回去用Options API了——就像开完电动车,谁还愿意蹬自行车啊?

    滚动至顶部