Vue.js: Vue 3.4 新特性

最后更新:2026-08-26

Vue 3.4+(2023-12 发布)带来了多项革命性改进defineModel 宏简化 v-model、响应式 props 解构、useTemplateRef 组合式 API、useId 生成唯一 ID。这些特性大幅简化模板代码,提升开发体验。

本课帮你掌握 5 大核心新特性,让你的 Vue 3 代码更简洁、更现代化。

1. 你将学到


2. 一个 v-model 组件的"父子同步"难题

(1) 痛点:5 行 props + 5 行 emit = 10 行 boilerplate

Alice 构建了一个带 v-model 的自定义输入组件:

JS
// ❌ 翻车版:v-model 需要 10 行样板代码
const props = defineProps({ modelValue: String })
const emit = defineEmits(['update:modelValue'])

const internalValue = ref(props.modelValue)
watch(() => props.modelValue, (val) => {
  internalValue.value = val
})
watch(internalValue, (val) => {
  emit('update:modelValue', val)
})

10 行样板代码,每个 v-model 组件都要写

技术负责人:

"Alice,这太啰嗦了。Vue 3.4 的 defineModel 一行就能搞定。"

(2) Vue 3.4 defineModel 宏解法

JS
// ✅ 正确版:1 行搞定 v-model
const modelValue = defineModel('modelValue')

// 现在可以:
// - 读:modelValue.value
// - 写:modelValue.value = 'new value'(自动 emit)
// - watch:watch(modelValue, ...)
// - 父组件 v-model 双向绑定照常工作

10 行 → 1 行。5 个 v-model 组件省 50 行代码。

(3) 收益

升级到 defineModel 后:


3. defineModel 宏详解

(1) 基本用法

VUE
<!-- 子组件:CustomInput.vue -->
<template>
  <input :value="modelValue" @input="modelValue = $event.target.value">
</template>

<script setup>
// ✅ 1 行 v-model
const modelValue = defineModel('modelValue')
</script>
VUE
<!-- 父组件:使用 v-model -->
<template>
  <CustomInput v-model="searchQuery" />
  <p>Search: {{ searchQuery }}</p>
</template>

<script setup>
const { ref } = Vue
const searchQuery = ref('')
</script>

(2) 5 大高级用法

JS
// 1. 默认值
const modelValue = defineModel('modelValue', { default: '' })

// 2. 类型约束(TypeScript)
const count = defineModel<number>('count', { default: 0 })

// 3. transform 转换(写时转换)
const trimmed = defineModel('text', {
  set(value) {
    return value.trim()  // 自动 trim
  }
})

// 4. getter(读时转换)
const upper = defineModel('text', {
  get(value) {
    return value.toUpperCase()  // 总是大写
  }
})

// 5. 多个 v-model
const name = defineModel('name')
const email = defineModel('email')
const phone = defineModel('phone')

(3) 完整示例

VUE
<!-- 子组件:TrimInput.vue -->
<template>
  <input :value="value" @input="value = $event.target.value">
</template>

<script setup>
const value = defineModel('value', {
  // get: 读时转换
  get(val) {
    return val?.toUpperCase() || ''
  },
  // set: 写时转换
  set(val) {
    return val?.trim() || ''
  },
  default: ''
})
</script>
VUE
<!-- 父组件 -->
<CustomInput v-model="name" />
<!-- 用户输入 "alice" → trim 不变 → uppercase → "ALICE" -->

4. 响应式 props 解构

(1) Vue 3.4 之前的痛点

JS
// ❌ Vue 3.3 及之前
const props = defineProps({ count: Number, name: String })

// 模板中直接用 props.count
// JS 中要 props.count
// 解构会丢失响应
const { count, name } = props  // count/name 变普通值

(2) Vue 3.4 解构保持响应

VUE
<!-- 子组件 -->
<template>
  <p>{{ count }} - {{ name }}</p>
  <button @click="count++">+1</button>
</template>

<script setup>
// ✅ Vue 3.4:解构后仍是响应式
const { count, name } = defineProps<{
  count: number
  name: string
}>()
</script>

解构的 props 保持响应式!这是 Vue 3.4 最大的改进之一。

(3) 5 大应用场景

JS
// 1. 简化 props 使用
const { count, name } = defineProps<{ count: number; name: string }>()

// 2. 解构 + 重命名
const { count: total, name: userName } = defineProps<{ count: number; name: string }>()

// 3. 解构 + 默认值
const { count = 0, name = 'Guest' } = defineProps<{ count?: number; name?: string }>()

// 4. 配合 watch(解构后 watch 仍工作)
const { count } = defineProps<{ count: number }>()
watch(count, (newVal) => console.log('count changed:', newVal))

// 5. 配合 computed(同样保持响应)
const { count } = defineProps<{ count: number }>()
const double = computed(() => count * 2)

(4) 注意事项

JS
// ⚠️ 解构的 props 不能再赋值
const { count } = defineProps<{ count: number }>()
count = 10  // ❌ 错误:Assignment to constant variable

// ✅ 正确:通过 emit 让父组件改
const emit = defineEmits(['update:count'])
const handleClick = () => emit('update:count', count + 1)

5. useId 唯一 ID

(1) 为什么需要 useId?

SSR(服务端渲染)时,手写 ID 会冲突(多个组件实例用同一个 ID)。useId 生成全局唯一且 SSR 安全的 ID。

JS
// ❌ 旧写法:手写 ID,SSR 会冲突
const id = `input-${Math.random()}`

// ✅ Vue 3.4+:useId
const { useId } = Vue
const id = useId()  // 'v-0', 'v-1', ...

(2) 5 大使用场景

VUE
<!-- 1. 表单 label 关联 -->
<template>
  <label :for="id">Username</label>
  <input :id="id" v-model="username">
</template>

<script setup>
const { useId } = Vue
const id = useId()
const username = ref('')
</script>

<!-- 2. ARIA 属性 -->
<template>
  <button :aria-describedby="`${id}-help`">Click</button>
  <p :id="`${id}-help`">This button does X</p>
</template>

<!-- 3. 表单错误信息 -->
<template>
  <input v-model="email" :aria-invalid="!!error" :aria-errormessage="`${id}-error`">
  <p v-if="error" :id="`${id}-error`">{{ error }}</p>
</template>

<!-- 4. Tooltip 关联 -->
<template>
  <button :aria-describedby="`${id}-tip`">?</button>
  <span :id="`${id}-tip`" class="tooltip">Help text</span>
</template>

<!-- 5. 模态框 -->
<template>
  <div :id="`${id}-modal`" role="dialog" aria-modal="true">
    ...
  </div>
</template>

(3) 5 大优势

优势 说明
SSR 安全 服务端/客户端 ID 一致
唯一 同页面多个实例不冲突
稳定 组件 re-render 不变
可读 ID 自带 v- 前缀易识别
轻量 0 依赖(Vue 内置)

6. useTemplateRef(Vue 3.5+ 详解)

(1) 完整特性

VUE
<template>
  <input ref="usernameInput">
  <MyChart ref="chartComponent" :data="chartData" />
</template>

<script setup lang="ts">
const { useTemplateRef, onMounted } = Vue
import MyChart from './MyChart.vue'

// ✅ Vue 3.5+:一个名字搞定
const inputRef = useTemplateRef<HTMLInputElement>('usernameInput')
const chartRef = useTemplateRef<InstanceType<typeof MyChart>>('chartComponent')

// ✅ TypeScript 完美推断
onMounted(() => {
  inputRef.value?.focus()  // HTMLInputElement 类型
  chartRef.value?.refresh()  // 组件实例方法
})
</script>

(2) Vue 3.5 vs Vue 3.4 对比

维度 Vue 3.4 变量 ref Vue 3.5 useTemplateRef
模板 ref <input ref="inputRef"> <input ref="usernameInput">
JS ref const inputRef = ref(null) const inputRef = useTemplateRef('usernameInput')
类型推断 需手动 ref<HTMLInputElement | null> 自动推断
名字一致性 可能拼错不报错 不一致会报错
简洁度 2 行 1 行

7. 5 大升级路径建议

(1) 升级时间

Vue 版本 发布时间 状态
Vue 3.0 2020-09 旧(建议升级)
Vue 3.2 2021-08 稳定
Vue 3.3 2023-05 稳定
Vue 3.4 2023-12 当前推荐
Vue 3.5 2024-09 最新稳定

(2) 升级清单

TEXT 📖 仅展示
✅ Vue 3.4:
  - defineModel 宏替代 v-model 复杂写法
  - 响应式 props 解构
  - 同名简写(<input :value> 替代 :value="value")
  - 错误处理改进
  - 模板解析器重写(2x 性能)

✅ Vue 3.5:
  - useTemplateRef(推荐)
  - 响应式 props 解构改进
  - useId 稳定
  - 延迟 teleport
  - 改进的 Suspense

(3) 升级建议

场景 建议
新项目 直接用 Vue 3.5+
老项目(Vue 3.3-) 升级到 3.5,按特性迁移
老项目(Vue 3.0-3.2) 升级到 3.3,再升 3.5
超大型项目 渐进式升级(先升级 Vue,再迁移 v-model 等)

8. 完整示例:Vue 3.4+ 综合应用

▶ 示例:v-model 双向绑定(CDN Vue 3.5 支持 :value + @input 简化)

HTML
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>

<style>
input { padding: 6px; border: 1px solid #ddd; border-radius: 4px; margin: 4px; }
.btn { padding: 6px 12px; background: #42b883; color: white; border: none; border-radius: 4px; cursor: pointer; }
</style>

<div id="app">
  <h4>1. v-model 双向绑定(CDN Vue 3.5)</h4>
  <input v-model="text" placeholder="输入文本">
  <p>实时显示: {{ text || '(空)' }}</p>

  <h4>2. 表单验证 + 错误提示</h4>
  <input v-model="email" placeholder="Email">
  <p v-if="error" style="color: #ef4444;">{{ error }}</p>
  <button class="btn" @click="validate">验证</button>
</div>

<script>
const { createApp, ref, computed } = Vue

const App = {
  setup() {
    const text = ref('')
    const email = ref('')
    const error = ref('')

    function validate() {
      if (!email.value || !email.value.includes('@')) {
        error.value = '❌ 邮箱格式错误'
      } else {
        error.value = ''
        alert('✅ 邮箱有效')
      }
    }

    return { text, email, error, validate }
  }
}

createApp(App).mount('#app')
</script>
▶ 试一试

▶ 示例:Vue 3.4+ 新特性速查(⚠️ 需 SFC + Vite,CDN 不可用)

⚠️ 以下示例需在 Vite + <script setup> 项目中使用,CDN Vue 3 全局构建不暴露 defineModel/useId/useTemplateRef 等编译宏。这些宏在 Vite 编译时被替换为运行时函数。

VUE
<!-- ⚠️ 需 Vite 项目,纯展示 -->

<!-- 1. defineModel 宏(替代 v-model 复杂写法) -->
<script setup>
// 1 行替代 10 行:props.modelValue + emit('update:modelValue')
const text = defineModel('text', { default: '' })
</script>
<template>
  <input v-model="text">
</template>

<!-- 2. 响应式 props 解构(Vue 3.4+) -->
<script setup>
// ✅ 解构后仍响应(默认深响应)
const { count, name } = defineProps<{
  count: number
  name: string
}>()
// 直接用 count, name,无需 props.count, props.name
</script>

<!-- 3. useId(SSR 安全的唯一 ID) -->
<script setup>
const { useId } = Vue
const id = useId()   // 自动生成 v-0, v-1, ...
</script>
<template>
  <label :for="id">Email</label>
  <input :id="id" type="email">
</template>

<!-- 4. useTemplateRef(Vue 3.5+ 强类型) -->
<script setup lang="ts">
const { useTemplateRef, onMounted } = Vue

// TypeScript 自动推断为 HTMLInputElement | null
const inputRef = useTemplateRef<HTMLInputElement>('usernameInput')

onMounted(() => {
  inputRef.value?.focus()
})
</script>
<template>
  <input ref="usernameInput" type="text">
</template>
▶ 试一试

▶ 示例:5 大常见错误速查

错误 现象 解决
defineModel 解构 失去响应 直接用 const modelValue = defineModel()
props 解构后赋值 报错 用 emit 让父改
useId 写固定字符串 SSR 冲突 改用 useId()
useTemplateRef 名字错 ref.value 是 null 名字必须匹配
升级 Vue 3.4+ 改太多 兼容性问题 渐进式迁移

▶ 示例:5 大 Vue 3.4+ 升级收益

收益 数据
代码量减少 30-50%(defineModel 简化)
性能提升 模板解析 2x 更快
类型推断 100% 准确(TypeScript)
SSR 友好 useId 解决 ID 冲突
开发者体验 DX 显著提升

❓ 常见问题

Q defineModel 替代 v-model 完整写法吗?
A 是的。defineModel('modelValue') 等同于 props.modelValue + emit('update:modelValue') 的 10 行代码。完全向后兼容。
Q 响应式 props 解构什么时候支持?
A Vue 3.4+(2023-12)。Vue 3.3 及以下不支持。解构后 props 仍响应(默认深响应)。
Q useId 每次渲染会变吗?
A 不会。useId 生成的 ID 在组件生命周期内保持稳定(SSR 和客户端一致)。
Q useTemplateRef vs ref 变量?
A useTemplateRef 是 Vue 3.5+ 引入的,TypeScript 推断更强,重命名更安全。普通项目用 ref 变量即可。
Q defineProps 解构能用于 watch 吗?
A 能。const { count } = defineProps() + watch(count, ...) 完全 work。
Q 如何从 Vue 3.3 升级到 3.4+?
A npm update vue@latest。代码层:defineModel 替代 v-model 复杂写法;响应式解构替代 props.xxx。零破坏。
Q Vue 3.5+ 对 TypeScript 有什么改进?
A defineProps 响应式解构 + useTemplateRef 完美类型推断 + 宏的 TS 支持更完善。推荐用 <script setup lang="ts">

📖 小节


📝 作业

  1. 基础题(难度⭐) 用 defineModel 重构一个 v-model 组件:

    • 子组件 CustomInput.vue:1 行 defineModel
    • 父组件:v-model 双向绑定
    • 支持默认值
  2. 进阶题(难度⭐⭐) 用 Vue 3.4+ 响应式解构重构组件:

    • 解构 count, name 等 props
    • 配合 watch + computed
    • 测试响应式保持
  3. 挑战题(难度⭐⭐⭐) 实现完整的 Vue 3.4+ 升级示例:

    1. 5 个组件用 defineModel 替代 v-model 复杂写法
    2. 3 个组件用响应式 props 解构
    3. 5 个组件用 useId(label/input 关联)
    4. 3 个组件用 useTemplateRef(Vue 3.5+)
    5. 5 个 TypeScript 强类型组件
    6. 测试 SSR 场景下 useId 的一致性
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏