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>