Vue 3 Composition API踩坑

15.1 Vue 3 ref和reactive的使用陷阱

核心知识点

  • Vue 3 ref和reactive的区别和使用场景
  • ref和reactive的响应式原理
  • ref和reactive的常见使用错误

常见错误场景

错误场景1:ref在模板中使用错误

<template>
  <div>
    <!-- 错误:在模板中使用ref的value属性 -->
    <h1>{{ count.value }}</h1>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

错误原因:在模板中使用ref时不需要使用.value属性,Vue会自动解包。

正确实现

<template>
  <div>
    <!-- 正确:在模板中直接使用ref -->
    <h1>{{ count }}</h1>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

错误场景2:reactive对象的解构问题

<template>
  <div>
    <h1>{{ user.name }}</h1>
    <button @click="updateName">更新名称</button>
  </div>
</template>

<script setup>
import { reactive } from 'vue'

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

// 错误:解构reactive对象会失去响应性
const { name } = user

function updateName() {
  name = 'Jane' // 这不会触发更新
}
</script>

错误原因:解构reactive对象会失去响应性,因为解构出来的是普通值。

正确实现

<template>
  <div>
    <h1>{{ user.name }}</h1>
    <button @click="updateName">更新名称</button>
  </div>
</template>

<script setup>
import { reactive } from 'vue'

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

function updateName() {
  user.name = 'Jane' // 正确:直接修改reactive对象的属性
}
</script>

15.2 Vue 3 computed和watch的常见错误

核心知识点

  • Vue 3 computed和watch的使用方法
  • computed和watch的依赖追踪
  • computed和watch的常见使用错误

常见错误场景

错误场景1:computed计算属性使用错误

<template>
  <div>
    <h1>{{ fullName }}</h1>
    <button @click="updateName">更新名称</button>
  </div>
</template>

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

const firstName = ref('John')
const lastName = ref('Doe')

// 错误:computed应该返回一个值
const fullName = computed(() => {
  console.log('Computing full name')
  // 错误:没有返回值
})

function updateName() {
  firstName.value = 'Jane'
}
</script>

错误原因:computed计算属性必须返回一个值,否则不会有正确的响应式行为。

正确实现

<template>
  <div>
    <h1>{{ fullName }}</h1>
    <button @click="updateName">更新名称</button>
  </div>
</template>

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

const firstName = ref('John')
const lastName = ref('Doe')

// 正确:computed返回一个值
const fullName = computed(() => {
  console.log('Computing full name')
  return `${firstName.value} ${lastName.value}`
})

function updateName() {
  firstName.value = 'Jane'
}
</script>

错误场景2:watch监听对象的错误

<template>
  <div>
    <h1>{{ user.name }}</h1>
    <button @click="updateUser">更新用户</button>
  </div>
</template>

<script setup>
import { reactive, watch } from 'vue'

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

// 错误:监听reactive对象的方式不正确
watch(user, (newUser, oldUser) => {
  console.log('User updated:', newUser)
})

function updateUser() {
  user.name = 'Jane'
}
</script>

错误原因:监听reactive对象时,默认只能监听到对象的引用变化,而不能监听到对象属性的变化。

正确实现

<template>
  <div>
    <h1>{{ user.name }}</h1>
    <button @click="updateUser">更新用户</button>
  </div>
</template>

<script setup>
import { reactive, watch } from 'vue'

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

// 正确:使用深度监听
watch(user, (newUser, oldUser) => {
  console.log('User updated:', newUser)
}, { deep: true })

// 或者使用watchEffect
// watchEffect(() => {
//   console.log('User name:', user.name)
// })

function updateUser() {
  user.name = 'Jane'
}
</script>

15.3 Vue 3 setup函数的使用误区

核心知识点

  • Vue 3 setup函数的使用方法
  • setup函数的执行时机
  • setup函数的返回值

常见错误场景

错误场景1:setup函数中使用this

<template>
  <div>
    <h1>{{ message }}</h1>
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  data() {
    return {
      message: 'Hello'
    }
  },
  setup() {
    // 错误:在setup函数中使用this
    console.log(this.message) // undefined
    
    const count = ref(0)
    return { count }
  }
}
</script>

错误原因:在setup函数中,this的值是undefined,不能访问组件实例。

正确实现

<template>
  <div>
    <h1>{{ message }}</h1>
    <p>{{ count }}</p>
  </div>
</template>

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

export default {
  setup() {
    // 正确:在setup中定义响应式数据
    const message = ref('Hello')
    const count = ref(0)
    
    return { message, count }
  }
}
</script>

错误场景2:setup函数返回值错误

<template>
  <div>
    <h1>{{ message }}</h1>
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  setup() {
    const message = ref('Hello')
    // 错误:没有返回响应式数据
  }
}
</script>

错误原因:setup函数必须返回一个对象,包含模板中需要使用的数据和方法。

正确实现

<template>
  <div>
    <h1>{{ message }}</h1>
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  setup() {
    const message = ref('Hello')
    // 正确:返回响应式数据
    return { message }
  }
}
</script>

15.4 Vue 3生命周期钩子的变化陷阱

核心知识点

  • Vue 3生命周期钩子的变化
  • Composition API中的生命周期钩子
  • 生命周期钩子的执行顺序

常见错误场景

错误场景1:使用Vue 2的生命周期钩子

<template>
  <div>
    <h1>{{ message }}</h1>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const message = ref('Hello')

// 错误:使用Vue 2的生命周期钩子
mounted() {
  console.log('Component mounted')
}
</script>

错误原因:在Composition API中,应该使用onMounted等新的生命周期钩子。

正确实现

<template>
  <div>
    <h1>{{ message }}</h1>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const message = ref('Hello')

// 正确:使用Vue 3的生命周期钩子
onMounted(() => {
  console.log('Component mounted')
})
</script>

错误场景2:生命周期钩子的执行时机错误

<template>
  <div>
    <h1>{{ data }}</h1>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const data = ref('Loading...')

// 错误:在setup中直接调用异步函数
fetchData()

onMounted(() => {
  console.log('Component mounted')
})

async function fetchData() {
  const response = await fetch('/api/data')
  const result = await response.json()
  data.value = result
}
</script>

错误原因:在setup中直接调用异步函数可能会导致数据在组件挂载前就被更新。

正确实现

<template>
  <div>
    <h1>{{ data }}</h1>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const data = ref('Loading...')

onMounted(async () => {
  // 正确:在onMounted中调用异步函数
  await fetchData()
  console.log('Component mounted')
})

async function fetchData() {
  const response = await fetch('/api/data')
  const result = await response.json()
  data.value = result
}
</script>

15.5 Vue 3 provide和inject的常见问题

核心知识点

  • Vue 3 provide和inject的使用方法
  • provide和inject的响应式传递
  • provide和inject的作用域

常见错误场景

错误场景1:provide的响应式数据传递错误

<!-- ParentComponent.vue -->
<template>
  <div>
    <h1>Parent Component</h1>
    <button @click="updateMessage">更新消息</button>
    <ChildComponent />
  </div>
</template>

<script setup>
import { ref, provide } from 'vue'
import ChildComponent from './ChildComponent.vue'

const message = ref('Hello from parent')

// 错误:直接provide ref对象
provide('message', message.value)

function updateMessage() {
  message.value = 'Updated message'
}
</script>

<!-- ChildComponent.vue -->
<template>
  <div>
    <h2>Child Component</h2>
    <p>{{ injectedMessage }}</p>
  </div>
</template>

<script setup>
import { inject } from 'vue'

const injectedMessage = inject('message')
</script>

错误原因:直接provide ref的value值会失去响应性,应该provide整个ref对象。

正确实现

<!-- ParentComponent.vue -->
<template>
  <div>
    <h1>Parent Component</h1>
    <button @click="updateMessage">更新消息</button>
    <ChildComponent />
  </div>
</template>

<script setup>
import { ref, provide } from 'vue'
import ChildComponent from './ChildComponent.vue'

const message = ref('Hello from parent')

// 正确:provide整个ref对象
provide('message', message)

function updateMessage() {
  message.value = 'Updated message'
}
</script>

<!-- ChildComponent.vue -->
<template>
  <div>
    <h2>Child Component</h2>
    <p>{{ injectedMessage }}</p>
  </div>
</template>

<script setup>
import { inject } from 'vue'

const injectedMessage = inject('message')
</script>

错误场景2:inject的默认值设置错误

<template>
  <div>
    <h2>Child Component</h2>
    <p>{{ injectedMessage }}</p>
  </div>
</template>

<script setup>
import { inject } from 'vue'

// 错误:inject的默认值设置方式错误
const injectedMessage = inject('message', 'Default message')

// 错误:使用函数作为默认值的方式错误
const injectedConfig = inject('config', () => ({ theme: 'light' }))
</script>

错误原因:inject的默认值设置方式错误,当使用函数作为默认值时需要使用第三个参数。

正确实现

<template>
  <div>
    <h2>Child Component</h2>
    <p>{{ injectedMessage }}</p>
    <p>{{ injectedConfig.theme }}</p>
  </div>
</template>

<script setup>
import { inject } from 'vue'

// 正确:设置简单的默认值
const injectedMessage = inject('message', 'Default message')

// 正确:使用函数作为默认值
const injectedConfig = inject('config', () => ({ theme: 'light' }), true)
</script>

15.6 Vue 3自定义组合式函数的陷阱

核心知识点

  • Vue 3自定义组合式函数的创建和使用
  • 组合式函数的命名规范
  • 组合式函数的响应式处理

常见错误场景

错误场景1:组合式函数的命名错误

// 错误:组合式函数的命名不符合规范
function useCounter() {
  const count = ref(0)
  
  function increment() {
    count.value++
  }
  
  return { count, increment }
}

// 错误:使用时的命名错误
const { count, increment } = useCounter()

错误原因:组合式函数的命名应该以use开头,使用驼峰命名法。

正确实现

// 正确:组合式函数的命名符合规范
function useCounter() {
  const count = ref(0)
  
  function increment() {
    count.value++
  }
  
  return { count, increment }
}

// 正确:使用组合式函数
const { count, increment } = useCounter()

错误场景2:组合式函数的响应式处理错误

// 错误:组合式函数没有正确处理响应式
function useUser() {
  let user = { name: 'John', age: 30 }
  
  function updateUser(newUser) {
    user = newUser
  }
  
  return { user, updateUser }
}

// 使用
const { user, updateUser } = useUser()
updateUser({ name: 'Jane', age: 25 })
// user不会更新

错误原因:组合式函数中的数据没有使用ref或reactive,导致失去响应性。

正确实现

// 正确:组合式函数正确处理响应式
function useUser() {
  const user = ref({ name: 'John', age: 30 })
  
  function updateUser(newUser) {
    user.value = newUser
  }
  
  return { user, updateUser }
}

// 使用
const { user, updateUser } = useUser()
updateUser({ name: 'Jane', age: 25 })
// user会更新

15.7 Vue 3响应式API的理解误区

核心知识点

  • Vue 3响应式API的原理
  • ref和reactive的区别
  • 响应式API的使用场景

常见错误场景

错误场景1:错误理解ref和reactive的适用场景

<template>
  <div>
    <h1>{{ user.name }}</h1>
    <button @click="updateUser">更新用户</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

// 错误:对于复杂对象,应该使用reactive
const user = ref({ name: 'John', age: 30, address: { street: '123 Main St', city: 'New York' } })

function updateUser() {
  // 错误:更新嵌套对象的方式
  user.value.address.city = 'Los Angeles'
}
</script>

错误原因:对于复杂对象,使用reactive会更方便,不需要使用.value。

正确实现

<template>
  <div>
    <h1>{{ user.name }}</h1>
    <p>{{ user.address.city }}</p>
    <button @click="updateUser">更新用户</button>
  </div>
</template>

<script setup>
import { reactive } from 'vue'

// 正确:对于复杂对象,使用reactive
const user = reactive({ 
  name: 'John', 
  age: 30, 
  address: { 
    street: '123 Main St', 
    city: 'New York' 
  } 
})

function updateUser() {
  // 正确:直接更新嵌套对象
  user.address.city = 'Los Angeles'
}
</script>

错误场景2:响应式API的性能误区

<template>
  <div>
    <h1>{{ heavyComputation }}</h1>
  </div>
</template>

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

const data = ref([])

// 错误:在computed中进行 heavy computation
const heavyComputation = computed(() => {
  let result = 0
  for (let i = 0; i < 1000000; i++) {
    result += i
  }
  return result
})
</script>

错误原因:在computed中进行重计算会影响性能,应该避免。

正确实现

<template>
  <div>
    <h1>{{ computedValue }}</h1>
    <button @click="calculateHeavy">计算</button>
  </div>
</template>

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

const data = ref([])
const heavyResult = ref(0)

const computedValue = computed(() => {
  return heavyResult.value
})

function calculateHeavy() {
  // 正确:在方法中进行重计算
  let result = 0
  for (let i = 0; i < 1000000; i++) {
    result += i
  }
  heavyResult.value = result
}
</script>

15.8 Vue 3 TypeScript支持的陷阱

核心知识点

  • Vue 3 TypeScript的配置和使用
  • Composition API的TypeScript类型定义
  • TypeScript的常见错误和解决方案

常见错误场景

错误场景1:TypeScript类型定义错误

<template>
  <div>
    <h1>{{ user.name }}</h1>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

// 错误:没有为ref指定类型
const user = ref({ name: 'John', age: 30 })

// 错误:类型定义错误
const count: ref<number> = ref(0)
</script>

错误原因:TypeScript类型定义错误,没有正确为ref指定类型。

正确实现

<template>
  <div>
    <h1>{{ user.name }}</h1>
    <p>{{ count }}</p>
  </div>
</template>

<script setup lang="ts">
import { ref, Ref } from 'vue'

// 正确:为ref指定类型
const user = ref<{ name: string; age: number }>({ name: 'John', age: 30 })

// 正确:使用Ref类型
const count: Ref<number> = ref(0)
</script>

错误场景2:接口定义错误

<template>
  <div>
    <h1>{{ product.name }}</h1>
    <p>{{ product.price }}</p>
  </div>
</template>

<script setup lang="ts">
import { reactive } from 'vue'

// 错误:接口定义错误
interface Product {
  name: string
  price: number
  stock?: number
}

// 错误:reactive的类型定义错误
const product = reactive<Product>({ name: 'Apple', price: 1.99 })
</script>

错误原因:接口定义错误或reactive的类型定义错误。

正确实现

<template>
  <div>
    <h1>{{ product.name }}</h1>
    <p>{{ product.price }}</p>
  </div>
</template>

<script setup lang="ts">
import { reactive } from 'vue'

// 正确:定义接口
interface Product {
  name: string
  price: number
  stock?: number
}

// 正确:使用reactive的类型定义
const product = reactive<Product>({ name: 'Apple', price: 1.99 })
</script>
« 上一篇 Vue生态系统踩坑 下一篇 » Vue Router 4踩坑