Vue3 Composition API使用及說明
一、setup 配置詳解
1.1 Vue2 中的 $attrs 和 $slots 回顧
在 Vue2 中,父組件通過標(biāo)簽屬性向子組件傳遞數(shù)據(jù)時,通常需要在子組件中使用 props 接收。但即使不聲明接收,這些屬性也會存在于子組件實例的 $attrs 中。
示例對比:
// Vue2 子組件
export default {
props: ['receivedProp'], // 聲明接收的屬性
created() {
console.log(this.$attrs) // 未聲明接收的屬性會出現(xiàn)在這里
console.log(this.$slots) // 父組件傳遞的插槽內(nèi)容
}
}1.2 Vue3 setup 的兩個關(guān)鍵點
執(zhí)行時機
setup在beforeCreate之前執(zhí)行- 此時組件實例尚未創(chuàng)建,
this為undefined
參數(shù)解析
setup 接收兩個參數(shù):
props:包含組件外部傳遞且內(nèi)部聲明接收的屬性
context:上下文對象,包含:
attrs:未在 props 中聲明的屬性(相當(dāng)于 Vue2 的$attrs)slots:接收的插槽內(nèi)容(相當(dāng)于 Vue2 的$slots)emit:觸發(fā)自定義事件的函數(shù)(相當(dāng)于 Vue2 的$emit)
完整示例:
import { defineComponent } from 'vue'
export default defineComponent({
props: ['title'],
emits: ['change'], // 必須聲明自定義事件
setup(props, { attrs, slots, emit }) {
console.log(props.title) // 訪問聲明的 prop
console.log(attrs.customAttr) // 訪問未聲明的屬性
const handleClick = () => {
emit('change', 'new value') // 觸發(fā)事件
}
return {
handleClick
}
}
})二、響應(yīng)式進階:計算屬性與監(jiān)視
2.1 computed 函數(shù)
Vue3 的 computed 用法與 Vue2 類似,但更靈活:
import { ref, computed } from 'vue'
setup() {
const firstName = ref('張')
const lastName = ref('三')
// 只讀計算屬性
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
// 可寫計算屬性
const editableName = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (newValue) => {
const [first, last] = newValue.split(' ')
firstName.value = first
lastName.value = last
}
})
return { fullName, editableName }
}2.2 watch 函數(shù)詳解
Vue3 的 watch 功能強大但有一些注意事項:
import { ref, reactive, watch } from 'vue'
setup() {
const count = ref(0)
const state = reactive({
user: {
name: 'Alice',
age: 25
}
})
// 情況1:監(jiān)視 ref
watch(count, (newVal, oldVal) => {
console.log(`count變化: ${oldVal} -> ${newVal}`)
})
// 情況2:監(jiān)視多個 ref
watch([count, anotherRef], ([newCount, newAnother], [oldCount, oldAnother]) => {
// 處理變化
})
// 情況3:監(jiān)視 reactive 對象(注意 oldValue 問題)
watch(
() => state.user,
(newUser, oldUser) => {
// oldUser 可能與 newUser 相同!
},
{ deep: true } // 雖然默認開啟,但顯式聲明更清晰
)
// 情況4:監(jiān)視 reactive 對象的特定屬性
watch(
() => state.user.age,
(newAge, oldAge) => {
// 這里能正確獲取 oldAge
}
)
}2.3 watchEffect 智能監(jiān)聽
watchEffect 自動追蹤回調(diào)中的響應(yīng)式依賴:
import { ref, watchEffect } from 'vue'
setup() {
const count = ref(0)
const message = ref('')
watchEffect(() => {
console.log(`count: ${count.value}, message: ${message.value}`)
// 會自動追蹤 count 和 message 的變化
})
// 實際應(yīng)用:自動取消之前的請求
const searchQuery = ref('')
watchEffect(async (onCleanup) => {
const query = searchQuery.value
const controller = new AbortController()
onCleanup(() => controller.abort()) // 清除副作用
if (query) {
const results = await fetchResults(query, {
signal: controller.signal
})
// 處理結(jié)果
}
})
}三、生命周期全解析
3.1 Vue2 與 Vue3 生命周期對比
| Vue2 生命周期 | Vue3 生命周期 (Options API) | Vue3 Composition API |
|---|---|---|
| beforeCreate | beforeCreate | setup() |
| created | created | setup() |
| beforeMount | beforeMount | onBeforeMount |
| mounted | mounted | onMounted |
| beforeUpdate | beforeUpdate | onBeforeUpdate |
| updated | updated | onUpdated |
| beforeDestroy | beforeUnmount | onBeforeUnmount |
| destroyed | unmounted | onUnmounted |
3.2 組合式 API 生命周期使用
import { onMounted, onUnmounted } from 'vue'
setup() {
// 鼠標(biāo)位置跟蹤示例
const x = ref(0)
const y = ref(0)
const updatePosition = (e) => {
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => {
window.addEventListener('mousemove', updatePosition)
})
onUnmounted(() => {
window.removeEventListener('mousemove', updatePosition)
})
return { x, y }
}四、自定義 Hook 實踐
自定義 Hook 是 Vue3 代碼復(fù)用的利器:
4.1 鼠標(biāo)位置跟蹤 Hook
// hooks/useMousePosition.js
import { ref, onMounted, onUnmounted } from 'vue'
export function useMousePosition() {
const x = ref(0)
const y = ref(0)
const updatePosition = (e) => {
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => {
window.addEventListener('mousemove', updatePosition)
})
onUnmounted(() => {
window.removeEventListener('mousemove', updatePosition)
})
return { x, y }
}
// 在組件中使用
import { useMousePosition } from './hooks/useMousePosition'
setup() {
const { x, y } = useMousePosition()
return { x, y }
}4.2 數(shù)據(jù)請求 Hook
// hooks/useFetch.js
import { ref, isRef, unref, watchEffect } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loading = ref(false)
const fetchData = async () => {
loading.value = true
try {
const response = await fetch(unref(url))
data.value = await response.json()
error.value = null
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
if (isRef(url)) {
watchEffect(fetchData)
} else {
fetchData()
}
return { data, error, loading, retry: fetchData }
}五、toRef 與 toRefs 深度解析
5.1 toRef 使用場景
import { reactive, toRef } from 'vue'
setup() {
const state = reactive({
name: 'Alice',
age: 25
})
// 保持響應(yīng)式連接
const nameRef = toRef(state, 'name')
setTimeout(() => {
state.name = 'Bob' // nameRef 也會更新
}, 1000)
return {
nameRef // 可以在模板中直接使用
}
}5.2 toRefs 解構(gòu)響應(yīng)式對象
import { reactive, toRefs } from 'vue'
setup() {
const state = reactive({
name: 'Alice',
age: 25,
address: {
city: 'Beijing'
}
})
// 解構(gòu)后仍保持響應(yīng)式
const { name, age } = toRefs(state)
// 嵌套對象需要單獨處理
const { city } = toRefs(state.address)
return {
name,
age,
city
}
}六、Vue3 新組件實戰(zhàn)
6.1 Fragment 片段組件
Vue3 不再需要根標(biāo)簽:
<template> <header>...</header> <main>...</main> <footer>...</footer> </template>
6.2 Teleport 傳送門
將組件渲染到 DOM 中的其他位置:
<template>
<button @click="showModal = true">打開彈窗</button>
<Teleport to="body">
<div v-if="showModal" class="modal">
<div class="modal-content">
<h2>標(biāo)題</h2>
<p>內(nèi)容...</p>
<button @click="showModal = false">關(guān)閉</button>
</div>
</div>
</Teleport>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const showModal = ref(false)
return { showModal }
}
}
</script>6.3 Suspense 異步組件
優(yōu)雅處理異步組件加載狀態(tài):
<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div class="loading-spinner">
加載中...
</div>
</template>
</Suspense>
</template>
<script>
import { defineAsyncComponent } from 'vue'
const AsyncComponent = defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
export default {
components: {
AsyncComponent
}
}
</script>七、Composition API 優(yōu)勢總結(jié)
- 更好的代碼組織:相關(guān)功能代碼集中在一起
- 更好的邏輯復(fù)用:通過自定義 Hook 實現(xiàn)
- 更好的類型推斷:對 TypeScript 支持更友好
- 更小的生產(chǎn)包:Tree-shaking 友好
對比示例:
// Options API 方式
export default {
data() {
return {
count: 0,
searchQuery: ''
}
},
computed: {
filteredList() {
// 基于 searchQuery 過濾列表
}
},
methods: {
increment() {
this.count++
}
},
mounted() {
// 初始化代碼
}
}
// Composition API 方式
import { ref, computed, onMounted } from 'vue'
export default {
setup() {
const count = ref(0)
const searchQuery = ref('')
const increment = () => {
count.value++
}
const filteredList = computed(() => {
// 過濾邏輯
})
onMounted(() => {
// 初始化代碼
})
return {
count,
searchQuery,
increment,
filteredList
}
}
}總結(jié)
Vue3 的 Composition API 為開發(fā)者提供了更靈活、更強大的代碼組織方式。通過本文的詳細解析和豐富示例,相信你已經(jīng)掌握了其核心概念和使用技巧。
在實際開發(fā)中,建議從簡單功能開始逐步嘗試組合式 API,體驗其帶來的開發(fā)效率提升和代碼可維護性優(yōu)勢。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue3.0使用mapState,mapGetters和mapActions的方式
這篇文章主要介紹了vue3.0使用mapState,mapGetters和mapActions的方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
Vue3組合式API之getCurrentInstance詳解
我們可以通過?getCurrentInstance這個函數(shù)來返回當(dāng)前組件的實例對象,也就是當(dāng)前vue這個實例對象,下面這篇文章主要給大家介紹了關(guān)于Vue3組合式API之getCurrentInstance的相關(guān)資料,需要的朋友可以參考下2022-09-09

