Vue3编程中更多常见书写错误场景
以下是Vue3编程中更多常见书写错误场景,涵盖响应式、组件、路由、状态管理、模板语法等多个维度:
错误场景分类 | 错误示例 | 问题所在 | 正确示例 |
---|---|---|---|
响应式API错误 | const arr = ref([1,2,3]); arr.push(4) | 对ref 数组直接调用数组方法无效(需通过.value 访问原始数组) | arr.value.push(4) |
响应式API错误 | shallowRef({ name: '张三' }); obj.value.name = '李四' | shallowRef 仅监听.value 的引用变化,修改内部属性不会触发更新 | 使用ref 或reactive ;若需浅层响应,修改时替换整个对象:obj.value = { name: '李四' } |
响应式API错误 | const state = reactive({ list: [] }); state.list = [1,2,3] | 直接替换reactive 对象的属性(数组)会丢失响应性(破坏了原始代理) | state.list.splice(0, state.list.length, ...[1,2,3]) (修改原数组) |
Props处理错误 | defineProps({ list: Array }) ,未指定默认值导致空值错误 | 未设置默认值时,父组件未传list 会导致list 为undefined ,调用数组方法报错 | defineProps({ list: { type: Array, default: () => [] } }) |
Props处理错误 | 子组件用watch(props.age, (newVal) => {}) | props 是响应式对象,但直接解构或访问其属性(如props.age )作为watch源可能不触发 | watch(() => props.age, (newVal) => {}) (用函数返回值作为源) |
计算属性错误 | const total = computed(() => { if (list.value.length) return sum; }) | 计算属性未处理所有分支返回值,可能导致返回undefined 引发渲染错误 | const total = computed(() => list.value.length ? sum : 0) (确保所有路径有返回值) |
计算属性错误 | const filteredList = computed(() => list.value.filter(...)) ,直接修改filteredList.value | 计算属性返回的是派生值,修改会导致不一致(应修改源数据) | 修改list.value ,让计算属性自动更新 |
模板语法错误 | <input v-model="user.name" :value="user.name"> | v-model 与:value 同时使用会冲突(v-model 已包含:value 和@input ) | 仅保留v-model="user.name" |
模板语法错误 | <div :class="{ active: isActive }, 'base-class'"> | :class 的对象语法与字符串语法混用格式错误(应放在数组中) | <div :class="[{ active: isActive }, 'base-class']"> |
模板语法错误 | <button @click="handleClick(1, $event)">点击</button> ,函数中未接收事件 | 传递$event 但函数未定义参数接收,导致无法获取事件对象 | const handleClick = (num, e) => { console.log(e) } |
自定义事件错误 | 子组件emit('update:modelValue', newVal) ,但父组件用v-model:value | 自定义组件v-model 默认绑定modelValue ,需统一名称或显式指定参数 | 子组件:emit('update:value', newVal) ,父组件:v-model:value="val" |
自定义事件错误 | defineEmits({ 'update:age': (val) => val > 0 }) ,验证失败未处理 | 事件验证函数返回false 时,事件不会触发,但未给出错误提示导致调试困难 | 验证函数中添加提示:(val) => { if (val <= 0) console.error('年龄无效'); return val > 0 } |
生命周期钩子错误 | onMounted(() => { const timer = setInterval(...); }) ,未清理定时器 | 组件卸载后定时器仍运行,导致内存泄漏 | onMounted(() => { const timer = setInterval(...); onUnmounted(() => clearInterval(timer)); }) |
生命周期钩子错误 | 在onBeforeMount 中操作DOM元素(如document.getElementById ) | onBeforeMount 阶段DOM尚未挂载,无法获取元素 | 在onMounted 中操作DOM |
组合式API错误 | const { data } = useRequest(); watch(data, (newVal) => {}) | data 可能是ref 但未通过函数返回,watch无法监听(需确保源是响应式的) | watch(() => data.value, (newVal) => {}) |
组合式API错误 | 在setup 中定义const handleClick = () => { ... } ,模板中用@click="handleClick()" | 函数无需加括号(加括号会每次渲染重新创建函数) | 模板中直接@click="handleClick" |
路由相关错误 | import { useRouter } from 'vue-router'; const route = useRouter() | 混淆useRouter (路由实例,用于导航)和useRoute (当前路由信息) | import { useRoute } from 'vue-router'; const route = useRoute() |
路由相关错误 | router.push({ path: '/user', params: { id: 1 } }) | path 与params 同时使用时,params 会被忽略(需用name 配合params ) | router.push({ name: 'user', params: { id: 1 } }) |
路由相关错误 | 在beforeRouteEnter 中访问this | beforeRouteEnter 钩子中this 未初始化,无法访问组件实例 | 通过next(vm => { vm.xxx = ... }) 访问组件实例vm |
Pinia状态管理错误 | defineStore('user', { state: () => ({ name: '' }), getters: { fullName: () => this.name } }) | getter中不能用this (应使用箭头函数或参数) | getters: { fullName: (state) => state.name } |
Pinia状态管理错误 | const userStore = useUserStore(); userStore.name = '李四' ,未在state定义 | 修改未在state 中声明的属性,不会触发响应式更新 | 在state 中显式定义name: '' ,再修改 |
Pinia状态管理错误 | 调用action:userStore.fetchUser() ,未处理异步错误 | 异步action可能失败,但未捕获错误导致应用崩溃 | try { await userStore.fetchUser() } catch (e) { console.error(e) } |
自定义指令错误 | app.directive('my-dir', (el, binding) => { ... }) ,未处理更新逻辑 | 函数式指令仅在mounted 和updated 时执行,但可能未正确区分两个阶段 | 用对象形式:{ mounted: () => {}, updated: () => {} } |
自定义指令错误 | v-my-dir="value" ,指令中用binding.value 但value 是响应式数据 | 指令不会自动追踪响应式数据变化,value 更新时指令不会重新执行 | 配合watch 在指令内监听value 变化 |
组件注册错误 | 全局注册组件:app.component('myComponent', MyComponent) | 组件名使用驼峰命名在模板中需转为kebab-case,可能导致引用错误(推荐全程kebab-case) | app.component('my-component', MyComponent) ,模板中<my-component> |
组件注册错误 | 在<script setup> 中局部注册组件:import MyComponent; components: { MyComponent } | <script setup> 中导入的组件会自动注册,无需手动声明components 选项 | 直接import MyComponent ,模板中使用<MyComponent> |
动态组件错误 | <component :is="currentComponent" :key="currentComponent" /> | key 与组件名绑定,切换时可能因组件名相同导致不重新渲染(应绑定唯一标识) | :key="componentId" (用唯一ID作为key) |
动态组件错误 | const currentComponent = ref('MyComponent') ,但组件未全局注册 | 字符串形式的is 需要组件已全局注册,否则无法识别 | 直接绑定组件对象:const currentComponent = ref(MyComponent) |
样式相关错误 | <style scoped> .child-class { color: red; } </style> ,修改子组件样式无效 | scoped 样式会限制仅作用于当前组件,无法直接修改子组件样式 | 使用深度选择器:::v-deep .child-class { color: red; } |
样式相关错误 | <style module> .active { ... } </style> ,模板中用class="active" | module 样式需通过$style 访问,直接使用类名无效 | <div :class="$style.active"> |
过渡动画错误 | <transition> <div v-if="show">内容</div> </transition> ,无动画效果 | 未定义过渡类(如v-enter-from 、v-enter-active )导致动画不生效 | 添加样式:.v-enter-active { transition: all 0.3s; } 等 |
过渡动画错误 | <transition-group> <div v-for="item in list" :key="item.id"> ,未设置过渡 | transition-group 默认不会为子元素添加过渡,需显式定义或使用name 属性 | <transition-group name="list"> ,并定义.list-enter-active 等样式 |
v-model修饰符错误 | 自定义组件中v-model.number ,未处理修饰符 | 父组件使用修饰符但子组件未解析,导致值类型错误(如字符串未转为数字) | 子组件defineProps({ modelValue: Number }) ,并在emit 前转换类型 |
v-model修饰符错误 | <input v-model.trim="name" v-model.lazy="name"> | 同一元素上使用多个v-model 会冲突(修饰符应合并) | <input v-model.trim.lazy="name"> |
事件修饰符错误 | <form @submit="handleSubmit"> ,未阻止默认提交行为 | 表单默认会刷新页面,需阻止默认行为 | <form @submit.prevent="handleSubmit"> |
事件修饰符错误 | <div @click.stop="handleClick"> ,嵌套元素也用@click.stop | 内层stop 会阻止事件冒泡到外层,可能导致外层事件无法触发 | 仅在需要阻止冒泡的层级使用stop |
依赖注入错误 | provide('user', ref('张三')) ,注入时const user = inject('user') ,直接修改user.value | 子组件修改注入的响应式数据,违反单向数据流(应通过函数修改) | 提供修改函数:provide('setUser', (val) => user.value = val) |
依赖注入错误 | inject('config') ,注入的是普通对象,修改后父组件无感知 | 注入的普通对象不具备响应性,修改不会同步到其他注入点 | 提供响应式对象:provide('config', reactive({})) |
TypeScript错误 | const count = ref(0); count.value = '123' | TypeScript环境下,ref 会自动推断类型,赋值不同类型会报错(需显式指定类型) | const count = ref<number / string>(0); count.value = '123' |
TypeScript错误 | defineProps<{ age: number }>() ,父组件传字符串age="18" | TypeScript类型校验失败,age 期望数字但接收字符串 | 父组件传数字:age="18"改为:age="18" ,或子组件兼容字符串类型(如defineProps<{ age: number |
异步组件错误 | const AsyncComponent = () => import('./AsyncComponent.vue') ,未处理加载状态 | 异步组件加载时无过渡,可能导致界面闪烁 | 使用defineAsyncComponent :const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue')) |
异步组件错误 | 异步组件加载失败,未设置错误回调 | 网络错误时组件加载失败,无错误提示导致用户体验差 | defineAsyncComponent({ loader: () => import(...), onError: (err) => console.error(err) }) |
这些场景覆盖了Vue3开发中从基础语法到高级特性的常见问题,涉及响应式原理、组件设计、路由交互、状态管理等核心环节。理解这些错误的本质(如响应式追踪机制、单向数据流、生命周期时机等),能更高效地排查问题并写出更健壮的代码。