Vue3新特性
刀刀
3/20/2025
0 字
0 分钟
Vue3 新特性官方指路:新特性 。
setup与Typescrupt
defineProps
更好的TS提示
在 Vue3 中,defineProps 能够更好的支持 TypeScript 类型提示,父组件引用子组件时能够有更好的 TypeScript 类型提示。代码示例如下:
设置一个 ts 类型
type.ts
tsexport interface type { msg: string }
子组件引入类型
vue<script setup ts> import type { type } from './type.ts' defineProps<type>() </script>
父组件使用子组件
vue<div class="App"> <h1>App</h1> <Hi /> </div>
运行后鼠标悬停在子组件上,可以看到其 defineProps 的类型提示。如果没有设置 msg,他还会报警告提示。
还可以进行一个扩展,代码如下:
<script setup lang="ts">
import type { Props } from './foo'
// imported + intersection type
defineProps<Props & { age: number }>()
</script>
现在多了一个 age 变量的类型支持。
注意
该功能需要 vscode 下载 1.7.0 版本以上的 Vue Language Features (Volar) 插件搭配使用。
更好的泛型支持
现在能够支持泛型设置,通过 generic
关键字来设置。步骤代码如下所示:
子组件通过
generic
关键字设置泛型vue<script setup lang="ts" generic="T"> defineProps<{ items: T[] selected: T }>() </script>
如果设置 T ,则该属性会根据父组件的传入自动推导 ts 类型。
也可以直接声明其类型,如下:
vue<script setup lang="ts" generic="T extends string | number"> defineProps<{ items: T[] selected: T }>() </script>
此时
items
是数字或字符串组合的数组,selected
是单个数组或字符串。父组件使用子组件
vue<div class="App"> <h1>App</h1> <Hi :selected="true" /> </div>
子组件设置泛型后父组件为
selected
传递一个布尔值,不符合条件,ts 校验会报相应的错误。
以上步骤是关于 setup
语法糖的设置方法,它同时也支持 defineComponents 的方法,方法步骤如下:
定义并导出一个 defineComponents 的泛型
jsximport { defineComponents } from 'vue' export default defineComponents (<T,> (props: {msg}) => { return () => <div>{ props.msg }</div> })
注意
泛型 T 后面的逗号可能是开发者留下来的小 BUG,不然会报错。等待其后续的修复
想要使用,需要提前在设置中打开
jsexport default defineConfig({ plugins: [ vue({ script: { propsDestructure: true } }) ] })
父组件引入
vue<GenericT></GenericT> <script setup> import GenericT from './component/GenericT' </script>
此时子组件也能拥有 ts 类型提示。
defineEmits
子组件通过 defineEmit 设置自定义事件现在也可以设置 ts 类型了,设置完类型后子组件如果没有传对应类型的参数会有报错;父组件没有接收对应类型的自定义事件也会报错。
代码如下所示:
子组件定义 defineEmits 及其传参的类型
jsconst emit = defineEmits<{ foo: [id: number] bar: [name: string] }>()
使用自定义事件时需要传对应的类型参数
自定义方法
foo
需要传一个参数,类型为数值型。自定义方法
bar
需要传一个参数,类型为字符串型。vue<button @click="handleClickFn"> 点我 </button> <script setup lang="ts"> const handleClickFn = () => { emit('foo', 1) } </script>
父组件使用
vue<Hi @bar="bar" @foo="foo" />
注意
也可以换一种写法,代码如下:
jsconst emit = defineEmits<{ (e: 'foo', id: number): void (e: 'bar', name: string): void }>()
写法可根据自己的项目选择。
defineSlot
纯粹的 ts 支持,专门用于设置类型。写法如下:
定义插槽需要的数据类型
jsdefineSlot<{ default: (props: {msg: string}) => any; foo: (props: {bar: number}) => any }>()
子组件设置插槽
vue<template> <div> <slot msg="nnnn"></slot> <slot name="daodao" :bar="1"></slot> </div> </template>
父组件使用插槽
vue<Sloter> <template v-slot="{msg}"> {{ msg }} </template> <template v-slot:foo="{bar, name}"> {{ bar, name }} </template> </Sloter>
注意
defineSlots()
只接受类型参数,不接受运行时参数。类型参数应为类型文本,其中属性键是槽名称,值是槽函数。函数的第一个参数是插槽期望接收的道具,其类型将用于模板中的槽道具。defineSlots
的返回值与从useSlots
返回的插槽对象相同。
实验性功能
Props 响应式解构
之前在解构 defineProps 的值时是没有响应式的,现在允许解构出来的值是响应式的。代码如下所示:
const { msg } = defineProps()
watchEffect(() => {
console.log(msg)
})
注意
上方代码变量
msg
实际上是props.msg
,所以能够监听获取到如果他传参给一个函数使用,则传参后的数据丢失了响应式,如下:
jsconst { msg } = defineProps() useXxx(msg) const useXxx = (msg) => { console.log(msg) }
defineModel 语法糖
以前,对于支持与 v-model
的双向绑定的组件,它需要:
- 声明一个 prop
- 在打算更新 prop 时发出相应的
update:propName
事件
代码如下:
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
console.log(props.modelValue)
function onInput(e) {
emit('update:modelValue', e.target.value)
}
</script>
<template>
<input :value="modelValue" @input="onInput" />
</template>
Vue3.3 通过新的 defineModel
宏简化了用法。宏会自动注册一个 prop,并返回一个可以直接变异的 ref:
<script setup>
const bar = defineModel()
console.log(bar.value)
</script>
<template>
<input v-model="bar" />
</template>
父组件使用:
<script setup>
const bar = ref('daodao')
</script>
<template>
<DefineModel v-model:bar="bar" />
</template>
此功能是实验性的,需要在 vite.config.js
文件中明确选择加入。
export default defineConfig({
plugins: [
vue({
script: {
//...
defineModel: true
}
})
]
})
其他功能
defineOptions
新的 defineOptions
宏允许直接在 <script setup>
中声明组件选项,而无需单独的 <script>
块:
<script setup>
defineOptions({
inheritAttrs: false,
name: 'daodao'
})
</script>
在之前想要给组件添加 name
属性不能写在 setup
中,只能另起一个 script
,如下:
<script setup></script>
<script>
export default {
name: 'daodao'
}
</script>
toRef
import { ref, toRef } from 'vue'
const himeSelf = ref(2)
const mySelfRef = toRef(1)
const himSelfRef = toRef(himeSelf)
const getterRef = toRef(() => 3)
前两者都是 ref
类型,没有区别,而最后一个是 getterRef
类型,只支持读不支持写操作。
使用 getter 调用 toRef
类似于 computed
,但当 getter 只是执行属性访问而没有昂贵的计算时,效率会更高。
使用场景:
之前在给函数传递解构出来的
props
参数会造成响应式丢失,现在通过toRef
可以继续保留响应式。jsconst props = defineProps<{ msg: string; foo: { bar: string }}>() const userXxxx = (e) => {} userXxxx(toRef(props.msg))
toValue
新的 toValue
实用程序方法提供了相反的方法,将值/getter/refs规范化为值:
toValue
可以在可组合项中使用,代替 unref
,以便可组合项可以接受 getter 作为反应式数据源:
import { ref, toValue } from 'vue'
const value1 = toValue(ref(1))
const value2 = toValue(2)
const value3 = toValue(() => 3)
JSX 导入源支持
目前,Vue 的类型会自动注册全局 JSX 类型。这可能会导致与其他需要 JSX 类型推理的库(特别是 React)一起使用的冲突。
从 3.3 开始,Vue 支持通过 TypeScript 的 jsxImportSource 选项指定 JSX 命名空间。这允许用户根据其用例选择全局或每个文件选择加入。
为了向后兼容,3.3 仍然全局注册 JSX 命名空间。我们计划在 3.4 中删除默认的全局注册。如果你在 Vue 中使用 TSX,你应该在升级到 3.3 后在你的 tsconfig.json
中添加显式 jsxImportSource
,以避免在 3.4 中出现中断。