Skip to content
alex's blog
Go back

Vue3 Composition API 入门指南

编辑页面

Vue3 引入了 Composition API,这是一个全新的组件编写方式,它提供了更好的逻辑复用、类型推断和代码组织能力。本文将带你从零开始学习 Composition API 的核心概念和用法。

什么是 Composition API?

Composition API 是 Vue3 提供的一套基于函数的 API,它允许我们通过组合函数的方式来组织组件逻辑,而不是通过选项(data、methods、computed 等)来组织。

为什么需要 Composition API?

在 Vue2 中,当组件变得复杂时,相关的逻辑会被分散在不同的选项中:

// Vue2 Options API 的问题
export default {
  data() {
    return {
      count: 0,
      message: 'Hello'
    }
  },
  methods: {
    increment() {
      this.count++
    },
    updateMessage() {
      this.message = 'Updated'
    }
  },
  computed: {
    doubleCount() {
      return this.count * 2
    }
  },
  // 相关的逻辑分散在不同地方
}

使用 Composition API,我们可以将相关的逻辑组织在一起:

// Vue3 Composition API
import { ref, computed } from 'vue'

export default {
  setup() {
    // 所有相关的逻辑都在这里
    const count = ref(0)
    const message = ref('Hello')
    
    const increment = () => {
      count.value++
    }
    
    const updateMessage = () => {
      message.value = 'Updated'
    }
    
    const doubleCount = computed(() => count.value * 2)
    
    return {
      count,
      message,
      increment,
      updateMessage,
      doubleCount
    }
  }
}

核心 API 详解

1. setup 函数

setup 是 Composition API 的入口函数,它在组件创建之前执行,接收 propscontext 两个参数:

export default {
  props: {
    title: String
  },
  setup(props, context) {
    // props 是响应式的,但不要解构它
    console.log(props.title)
    
    // context 包含 attrs、slots、emit
    const { attrs, slots, emit } = context
    
    return {
      // 返回的内容可以在模板中使用
    }
  }
}

2. ref 和 reactive

refreactive 是创建响应式数据的两种方式:

ref - 用于基本类型和对象引用:

import { ref } from 'vue'

const count = ref(0)
const user = ref({ name: 'John', age: 30 })

// 访问值需要使用 .value
console.log(count.value) // 0
count.value++

// 在模板中会自动解包,不需要 .value

reactive - 用于对象和数组:

import { reactive } from 'vue'

const state = reactive({
  count: 0,
  user: {
    name: 'John',
    age: 30
  }
})

// 直接访问,不需要 .value
console.log(state.count) // 0
state.count++

3. computed 计算属性

computed 用于创建计算属性:

import { ref, computed } from 'vue'

const count = ref(0)
const doubleCount = computed(() => count.value * 2)

// 只读计算属性
console.log(doubleCount.value) // 0

// 可写计算属性
const fullName = computed({
  get() {
    return `${firstName.value} ${lastName.value}`
  },
  set(newValue) {
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})

4. watch 和 watchEffect

watch - 监听特定数据源的变化:

import { ref, watch } from 'vue'

const count = ref(0)
const message = ref('')

// 监听单个源
watch(count, (newVal, oldVal) => {
  console.log(`count changed from ${oldVal} to ${newVal}`)
})

// 监听多个源
watch([count, message], ([newCount, newMessage], [oldCount, oldMessage]) => {
  console.log('count or message changed')
})

// 深度监听对象
const state = reactive({ user: { name: 'John' } })
watch(() => state.user, (newVal) => {
  console.log('user changed')
}, { deep: true })

watchEffect - 自动追踪依赖并执行:

import { ref, watchEffect } from 'vue'

const count = ref(0)

watchEffect(() => {
  // 自动追踪 count,当 count 变化时重新执行
  console.log(`count is ${count.value}`)
})

5. 生命周期钩子

Composition API 提供了对应的生命周期函数:

import { onMounted, onUpdated, onUnmounted } from 'vue'

export default {
  setup() {
    onMounted(() => {
      console.log('组件已挂载')
    })
    
    onUpdated(() => {
      console.log('组件已更新')
    })
    
    onUnmounted(() => {
      console.log('组件已卸载')
    })
  }
}

实际应用示例

示例 1: 计数器组件

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double: {{ doubleCount }}</p>
    <button @click="increment">+1</button>
    <button @click="decrement">-1</button>
  </div>
</template>

<script>
import { ref, computed } from 'vue'

export default {
  setup() {
    const count = ref(0)
    
    const increment = () => {
      count.value++
    }
    
    const decrement = () => {
      count.value--
    }
    
    const doubleCount = computed(() => count.value * 2)
    
    return {
      count,
      increment,
      decrement,
      doubleCount
    }
  }
}
</script>

示例 2: 数据获取

import { ref, onMounted } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const loading = ref(false)
  const error = ref(null)
  
  const fetchData = async () => {
    loading.value = true
    error.value = null
    try {
      const response = await fetch(url)
      data.value = await response.json()
    } catch (e) {
      error.value = e
    } finally {
      loading.value = false
    }
  }
  
  onMounted(() => {
    fetchData()
  })
  
  return {
    data,
    loading,
    error,
    refetch: fetchData
  }
}

最佳实践

  1. 使用 <script setup> 语法糖(Vue3.2+):
<script setup>
import { ref, computed } from 'vue'

const count = ref(0)
const doubleCount = computed(() => count.value * 2)
</script>
  1. 提取可复用的逻辑到 composables
// composables/useCounter.js
import { ref } from 'vue'

export function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  
  const increment = () => count.value++
  const decrement = () => count.value--
  const reset = () => count.value = initialValue
  
  return {
    count,
    increment,
    decrement,
    reset
  }
}
  1. 合理使用 ref 和 reactive
    • 基本类型使用 ref
    • 对象和数组可以使用 reactive,但 ref 更通用

总结

Composition API 为 Vue3 带来了更强大的逻辑组织和复用能力。通过合理使用 setuprefreactivecomputedwatch 等 API,我们可以编写出更清晰、更易维护的组件代码。

虽然学习曲线可能比 Options API 陡峭一些,但一旦掌握,你会发现它带来的好处是值得的。建议在实际项目中逐步迁移,先从新组件开始使用 Composition API。


希望这篇文章能帮助你更好地理解和使用 Vue3 Composition API。如果你有任何问题或建议,欢迎在评论区讨论!


编辑页面
Share this post on:

Previous Post
Vue3 响应式系统深度解析