Skip to content

Vue 大版本更新 ​

Vue3.3 的新功能的体验(上)props导入外部类型、DefineOptions、defineModel、Props 的响应式解构

Vue3.3 ​

defineOptions ​

<script setup> 中定义组件选项(name、inheritAttrs 等),无需额外写一个 <script> 块。

vue
<script setup lang="ts">
defineOptions({
  name: 'Foo',
  inheritAttrs: false
})
</script>

defineSlots ​

为插槽提供类型声明,让 IDE 对 $slots 和 useSlots() 提供类型提示。

vue
<script setup lang="ts">
defineSlots<{
  default(props: { msg: string }): any
  header(props: { title: string }): any
}>()
</script>

defineModel(实验性) ​

Vue3.3 里属于实验性宏,默认关闭,必须手动开启。

ts
// vite.config.ts
export default defineConfig({
  plugins: [
    vue({
      script: {
        defineModel: true // 手动开启 3.3 的 defineModel
      }
    })
  ]
})

父传子是单向数据流,无法直接通过子组件修改父组件传递的数据。defineModel 简化了 v-model 的双向绑定写法。

vue
<!-- 子组件 -->
<script setup lang="ts">
// 3.3 实验性写法
const modelValue = defineModel<string>()
</script>

<template>
  <input v-model="modelValue" />
</template>

泛型组件 ​

<script setup> 支持泛型参数,配合 TypeScript 实现类型安全的通用组件。

vue
<script setup lang="ts" generic="T">
defineProps<{
  items: T[]
  selected: T
}>()
</script>

从外部导入 Props 类型 ​

Vue3.3 支持 defineProps 从外部导入类型,不再局限于当前文件。

vue
<script setup lang="ts">
import type { Props } from './types'

defineProps<Props>()
</script>

Vue3.4 ​

defineModel 稳定化 ​

移除了实验性标识,无需任何手动配置,直接可用。同时支持多个 v-model。

vue
<!-- 父组件 -->
<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 同名简写 ​

当属性名和变量名相同时,可以省略属性值。

vue
<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 选项 ​

一次性监听,回调只触发一次后自动停止。

vue
<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)。

vue
<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 中直接使用。

vue
<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 的模式。类型推断更准确。

vue
<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 等)。

vue
<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,可自定义修饰符行为。

vue
<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>
vue
<!-- 父组件使用修饰符 -->
<Child v-model.upper="name" />
<Child v-model.trim="title" />

响应式 Props 解构默认值 ​

解构 props 时直接写默认值,不再需要 withDefaults。

vue
<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),同时显著降低运行时体积
  • 状态:实验阶段,面向性能敏感场景
vue
<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 树,打包体积更小、渲染更快。

常备不懈,才能有备无患