一、为什么需要 Composition API
Options API 在组件变大时,同一个功能的代码被拆分到data、methods、computed、watch 中,阅读时需要反复跳转。Composition API 让相关逻辑聚合在一起。
二、ref 与 reactive 的选择
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
// 基本类型用 ref
const count = ref(0)
const name = ref('')
// 对象类型二者皆可
const user = reactive({ name: 'Tom', age: 18 })
const form = ref({ title: '', content: '' })
// reactive 解构会丢失响应式!
const { name: n1 } = user // ❌ 丢失
const name2 = toRef(user, 'name') // ✅ 保持
</script>
经验法则:基本类型用 ref,对象用 reactive,需要整体替换时用 ref。
三、watch 与 watchEffect
// watch:明确指定监听源,可拿到新旧值
watch(count, (newVal, oldVal) => {
console.log(`count: ${oldVal} -> ${newVal}`)
})
// 监听多个
watch([count, name], ([c, n]) => { /* ... */ })
// watchEffect:自动收集依赖,立即执行一次
watchEffect(() => {
document.title = `${name.value} - ${count.value}`
})
// 停止监听
const stop = watchEffect(() => {})
stop()
四、生命周期映射
| Options API | Composition API | | --- | --- | | beforeCreate | setup() | | created | setup() | | beforeMount | onBeforeMount | | mounted | onMounted | | beforeUpdate | onBeforeUpdate | | updated | onUpdated | | beforeUnmount | onBeforeUnmount | | unmounted | onUnmounted |五、封装一个 useRequest
// composables/useRequest.ts
import { ref, onMounted } from 'vue'
export function useRequest<T>(url: string) {
const data = ref<T | null>(null)
const loading = ref(false)
const error = ref<Error | null>(null)
const run = async () => {
loading.value = true
error.value = null
try {
const res = await fetch(url)
data.value = await res.json()
} catch (e) {
error.value = e as Error
} finally {
loading.value = false
}
}
onMounted(run)
return { data, loading, error, run }
}
使用:
<script setup lang="ts">
const { data, loading, run } = useRequest<Article[]>('/api/articles')
</script>
<template>
<div v-if="loading">加载中...</div>
<ul v-else>
<li v-for="a in data" :key="a.id">{{ a.title }}</li>
</ul>
<button @click="run">刷新</button>
</template>
六、最佳实践
- composable 命名统一
useXxx - 一个 composable 只做一件事
- 副作用要在
onUnmounted中清理(定时器、事件监听) - 用
readonly暴露只读状态,避免外部误改
评论(2)