×

Vue3 新手必看的 10 个避坑知识点

独孤求败 独孤求败 发表于2026-06-18 22:35:04 浏览36 评论0

抢沙发发表评论

Vue3 新手必看的 10 个避坑知识点

Vue3 搭配 <script setup> 极大简化了开发,但响应式、API 用法、生命周期、组件通信、样式穿透等细节,新手极易踩坑。下面整理 10 个高频必避坑点,附错误写法、正确写法+总结,直接收藏可用。

1. 分不清 ref 和 reactive 适用场景

坑点

  1. 用 reactive 存字符串、数字、布尔基本类型,响应式失效
  2. 直接解构 reactive 对象,丢失响应式
  3. 全部混用,代码维护混乱

正确用法

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

// 基础类型:string / number / boolean → 用 ref
const count = ref(0)

// 对象、数组 → 推荐用 reactive
const user = reactive({ name: '张三', age: 20 })

// 解构保持响应式,必须搭配 toRefs
const { name, age } = toRefs(user)
</script>

避坑口诀

基础类型用 ref,对象数组用 reactive,解构必用 toRefs

2. script 里使用 ref 忘记加 .value

坑点

const count = ref(0)
count++ // ❌ 无效,视图不更新
console.log(count) // 拿到的是 ref 包装对象

正确写法

const count = ref(0)
count.value++
console.log(count.value)

注意

模板内自动解包不用 .valuescript 逻辑里必须加 .value

3. 直接修改 Props 传值

坑点

<script setup>
const props = defineProps({ num: Number })
props.num++ // ❌ 违规,单向数据流,控制台告警
</script>

正确做法

  • 只读:用 ref 本地拷贝一份
  • 需要修改:用 defineEmits 通知父组件变更
<script setup>
const props = defineProps({ num: Number })
const emit = defineEmits(['update:num'])

// 本地拷贝
const localNum = ref(props.num)

// 通知父组件更新
const changeNum = () => {
  emit('update:num', localNum.value + 1)
}
</script>

图片

4. 直接赋值 / 解构导致响应式丢失

坑点

// 1. 直接解构 reactive 丢失响应式
const user = reactive({ name'李四' })
const { name } = user

// 2. 直接覆盖 reactive 整个对象
const list = reactive([1,2,3])
list = [4,5,6]

正确写法

// 解构用 toRefs
const { name } = toRefs(user)

// 需要整体替换,改用 ref
const list = ref([1,2,3])
list.value = [4,5,6]

5. 还在用 Vue2 生命周期写法

坑点

<script setup> 没有 createdmounted 选项式写法,直接写会无效。

Vue3 组合式生命周期

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

// 组件挂载完成
onMounted(() => {
  // 接口请求、DOM 操作
})

// 组件更新完成
onUpdated(() => {})

// 组件销毁
onUnmounted(() => {})

顶层代码等价于 Vue2 的 created,无需额外写钩子。

图片

6. 计算属性 computed 漏写 return、当作方法调用

坑点

// ❌ 无 return
const fullName = computed(() => {
  firstName + lastName
})
// ❌ 错误调用
fullName()

正确写法

const fullName = computed(() => {
  return firstName.value + lastName.value
})
// 直接取值 .value
console.log(fullName.value)

7. watch 监听嵌套对象不开启 deep

坑点

const user = reactive({ info: { age18 } })
// ❌ 监听不到嵌套属性变化
watch(user, () => {
  console.log('变化了')
})

正确写法

// 监听嵌套对象,开启 deep
watch(user, () => {
  console.log('变化了')
}, { deeptrue })

// 监听 ref 不要加 .value
watch(count, (newVal) => {
  console.log(newVal)
})

8. script setup 忘记导入 Vue API

坑点

直接使用 ref、reactive、computed、watch 报未定义。

常用必导入

import {
  ref,
  reactive,
  computed,
  watch,
  onMounted,
  toRefs,
  defineProps,
  defineEmits,
  defineExpose
from 'vue'

9. 父组件拿不到子组件的方法与变量

坑点

<script setup> 作用域封闭,子组件变量和方法默认不对外暴露,父组件 ref 访问不到。

子组件

<script setup>
const testFn = () => {
  alert('子组件方法')
}
// 暴露给父组件
defineExpose({ testFn })
</script>

父组件

<Child ref="childRef" />

<script setup>
const childRef = ref(null)
const callChildFn = () => {
  childRef.value.testFn()
}
</script>

图片


10. 样式穿透还用 Vue2 的 /deep/ >>>

坑点

Vue3 中 /deep/>>> 已失效,样式穿透不生效。

正确写法

/* Vue3 统一使用 :deep() */
:deep(.child-wrap .item) {
  color#f00;
  font-size14px;
}

快速总结速记

  • 基础类型用 ref,对象数组用 reactive
  • script 中 ref 必加 .value
  • 禁止直接修改 props,用 emit 通信
  • 解构用 toRefs,避免响应式丢失
  • 生命周期用 onMounted 等组合式 API
  • computed 必须 return,不能加括号调用
  • 监听嵌套对象配置 deep: true
  • 所有 Vue API 手动导入
  • 子组件对外暴露方法用 defineExpose
  • 样式穿透统一用 :deep()


群贤毕至

访客