Vue 大版本更新
Vue3.3 的新功能的体验(上)props导入外部类型、DefineOptions、defineModel、Props 的响应式解构
Vue3.3
defineOptions
<script setup> 中定义组件选项(name、inheritAttrs 等),无需额外写一个 <script> 块。
<script setup lang="ts">
defineOptions({
name: 'Foo',
inheritAttrs: false
})
</script>defineSlots
为插槽提供类型声明,让 IDE 对 $slots 和 useSlots() 提供类型提示。
<script setup lang="ts">
defineSlots<{
default(props: { msg: string }): any
header(props: { title: string }): any
}>()
</script>defineModel(实验性)
Vue3.3 里属于实验性宏,默认关闭,必须手动开启。
// vite.config.ts
export default defineConfig({
plugins: [
vue({
script: {
defineModel: true // 手动开启 3.3 的 defineModel
}
})
]
})父传子是单向数据流,无法直接通过子组件修改父组件传递的数据。defineModel 简化了 v-model 的双向绑定写法。
<!-- 子组件 -->
<script setup lang="ts">
// 3.3 实验性写法
const modelValue = defineModel<string>()
</script>
<template>
<input v-model="modelValue" />
</template>泛型组件
<script setup> 支持泛型参数,配合 TypeScript 实现类型安全的通用组件。
<script setup lang="ts" generic="T">
defineProps<{
items: T[]
selected: T
}>()
</script>从外部导入 Props 类型
Vue3.3 支持 defineProps 从外部导入类型,不再局限于当前文件。
<script setup lang="ts">
import type { Props } from './types'
defineProps<Props>()
</script>Vue3.4
defineModel 稳定化
移除了实验性标识,无需任何手动配置,直接可用。同时支持多个 v-model。
<!-- 父组件 -->
<Child v-model="name" v-model:title="title" />
<!-- 子组件 -->
<script setup lang="ts">
const modelValue = defineModel<string>()
const title = defineModel<string>('title')
</script>
<template>
<input v-model="modelValue" />
<input v-model="title" />
</template>v-bind 同名简写
当属性名和变量名相同时,可以省略属性值。
<template>
<!-- 旧写法 -->
<div :id="id" :class="cls"></div>
<!-- 新写法 -->
<div :id :class></div>
</template>
<script setup lang="ts">
const id = ref('my-id')
const cls = ref('container')
</script>watch 的 once 选项
一次性监听,回调只触发一次后自动停止。
<script setup lang="ts">
import { ref, watch } from 'vue'
const source = ref(0)
watch(source, (val) => {
console.log('只触发一次:', val)
}, { once: true })
</script>onWatcherCleanup
在 watch 回调内部注册清理函数(替代 onCleanup)。
<script setup lang="ts">
import { ref, watch, onWatcherCleanup } from 'vue'
const keyword = ref('')
watch(keyword, async (val) => {
let aborted = false
onWatcherCleanup(() => {
aborted = true
})
const data = await fetchData(val)
if (!aborted) {
// 处理数据
}
})
</script>改进的响应式系统
computed 值现在在触发副作用前会先计算,减少了不必要的重渲染。watchEffect 依赖追踪更精确。
错误信息优化
构建时错误信息更加清晰,能准确指出代码中的问题位置。
Vue3.5
Props 响应式解构
解构后的 props 变量仍然保持响应式,可以在 watch 和 computed 中直接使用。
<script setup lang="ts">
const { count = 0, title } = defineProps<{
count?: number
title: string
}>()
// 解构后的变量仍然响应式
watch(() => count, (val) => {
console.log('count 变了:', val)
})
const double = computed(() => count * 2)
</script>
<template>
<p>{{ title }}: {{ count }}</p>
<p>双倍: {{ double }}</p>
</template>useTemplateRef
更清晰的模板引用 API,替代手动 ref() + template ref 的模式。类型推断更准确。
<script setup lang="ts">
import { useTemplateRef, onMounted } from 'vue'
// 旧写法
// const inputRef = ref<HTMLInputElement | null>(null)
// 新写法
const inputRef = useTemplateRef<HTMLInputElement>('input')
onMounted(() => {
inputRef.value?.focus()
})
</script>
<template>
<input ref="input" />
</template>useId
生成唯一 ID,支持 SSR 同构,用于无障碍属性(aria-labelledby 等)。
<script setup lang="ts">
import { useId } from 'vue'
const id = useId()
</script>
<template>
<div :id="id">
<label :for="`${id}-input`">用户名</label>
<input :id="`${id}-input`" />
</div>
</template>defineModel 支持 modifiers
解构出 model 值和 modifiers,可自定义修饰符行为。
<script setup lang="ts">
const [model, modifiers] = defineModel<string>({
set(val) {
if (modifiers.upper) return val.toUpperCase()
if (modifiers.trim) return val.trim()
return val
}
})
</script>
<template>
<input v-model="model" />
</template><!-- 父组件使用修饰符 -->
<Child v-model.upper="name" />
<Child v-model.trim="title" />响应式 Props 解构默认值
解构 props 时直接写默认值,不再需要 withDefaults。
<script setup lang="ts">
// 旧写法
// const props = withDefaults(defineProps<{ count?: number }>(), { count: 0 })
// 新写法
const { count = 0 } = defineProps<{ count?: number }>()
</script>服务端渲染改进
useId、useTemplateRef 等 API 天然支持 SSR,水合过程更稳定。
Vue Vapor Mode(实验性)
Vapor Mode 是 Vue 正在开发的一种新的编译策略,它跳过 Virtual DOM,直接生成更高效的 DOM 操作代码,以更小的运行时体积获得更好的性能。
- 目标:保留 Vue 现有的开发体验(SFC、响应式、组合式 API),同时显著降低运行时体积
- 状态:实验阶段,面向性能敏感场景
<script setup lang="ts" vapor>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">
{{ count }}
</button>
</template>Vapor 模式通过 defineVaporComponent 或 <script setup vapor> 标记启用,编译器直接生成操作真实 DOM 的代码,运行时无需维护 VNode 树,打包体积更小、渲染更快。