Skip to content
⚠️ This article was written in 2020. Some content may be outdated.

Vue 3 Composition API 深度解析

Vue 3 的 Composition API 是自 Vue 誕生以來最重大的範式轉變。對於習慣了 Options API 的開發者來說,這套 API 的學習曲線並不陡峭,但要真正用好它,需要理解其設計哲學和底層機制。本文從實際工程角度出發,剖析 Composition API 的核心用法。

setup 函式與響應式基礎

setup 是 Composition API 的入口,它在元件建立之前執行,接收 propscontext 兩個引數。所有響應式狀態、計算屬性、方法都在這裡定義。

javascript
import { ref, reactive, toRefs } from 'vue'

export default {
  props: {
    userId: {
      type: Number,
      required: true
    }
  },
  setup(props, { emit, attrs, slots }) {
    // ref 用於基本型別
    const count = ref(0)

    // reactive 用於物件
    const state = reactive({
      username: '',
      posts: [],
      loading: false
    })

    // 直接修改 reactive 物件的屬性即可觸發更新
    const fetchUser = async () => {
      state.loading = true
      const res = await fetch(`/api/users/${props.userId}`)
      const data = await res.json()
      state.username = data.name
      state.posts = data.posts
      state.loading = false
    }

    fetchUser()

    // 如果要用模板解構,需要 toRefs
    return {
      count,
      increment: () => count.value++,
      ...toRefs(state)
    }
  }
}

注意 ref 返回的物件在 JS 中訪問需要 .value,在模板中則自動解包。這是很多初學者容易踩的坑。

自定義可複用邏輯

Composition API 最強大的地方在於邏輯複用。過去用 mixins 會有命名衝突和來源不明的問題,現在用 composables 可以清晰地管理依賴。

javascript
// composables/useMousePosition.js
import { ref, onMounted, onUnmounted } from 'vue'

export function useMousePosition() {
  const x = ref(0)
  const y = ref(0)

  const update = (e) => {
    x.value = e.pageX
    y.value = e.pageY
  }

  onMounted(() => {
    window.addEventListener('mousemove', update)
  })

  onUnmounted(() => {
    window.removeEventListener('mousemove', update)
  })

  return { x, y }
}

// 使用
import { useMousePosition } from './composables/useMousePosition'

export default {
  setup() {
    const { x, y } = useMousePosition()
    return { x, y }
  }
}

watchEffect 與 watch 的選擇

watchEffect 會自動追蹤函式內的響應式依賴,首次立即執行。watch 則需要顯式指定資料來源。

javascript
import { ref, watch, watchEffect } from 'vue'

export default {
  setup(props) {
    const keyword = ref('')
    const results = ref([])

    // watchEffect: 自動追蹤 keyword,首次立即執行
    watchEffect(async () => {
      if (!keyword.value) return
      const res = await fetch(`/api/search?q=${keyword.value}`)
      results.value = await res.json()
    })

    // watch: 顯式指定依賴,可以拿到新舊值
    watch(keyword, (newVal, oldVal) => {
      console.log(`搜尋詞從 "${oldVal}" 變為 "${newVal}"`)
    })

    return { keyword, results }
  }
}

選擇建議:需要獲取舊值或精確控制觸發條件時用 watch;需要自動追蹤多個依賴時用 watchEffect

生命週期鉤子的對映

Composition API 中的生命週期鉤子都以 on 開頭,且只在 setup 中有效。

javascript
import { onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUpdate, onBeforeUnmount } from 'vue'

export default {
  setup() {
    onBeforeMount(() => {
      console.log('元件即將掛載')
    })

    onMounted(() => {
      console.log('元件已掛載,可以訪問 DOM')
    })

    onBeforeUpdate(() => {
      console.log('元件即將更新')
    })

    onUpdated(() => {
      console.log('元件已更新')
    })

    onBeforeUnmount(() => {
      console.log('元件即將解除安裝')
    })

    onUnmounted(() => {
      console.log('元件已解除安裝')
    })
  }
}

小結

  • Composition API 的核心是 setup 函式,所有邏輯從這裡組織
  • ref 用於基本型別,reactive 用於物件,注意 .value 的使用場景
  • Composables 替代 mixins,解決了命名衝突和來源不明的問題
  • watchEffect 自動追蹤依賴,watch 顯式指定且可獲取舊值
  • 生命週期鉤子全部以 on 字首命名,只能在 setup 中呼叫

MIT Licensed