Vue3.3 新特性总结
- 支持类型导入(defineProps / defineEmits)
- 泛型(defineProps / defineComponent )
- defineProps 的解构
- defienSlots
- defineEmits
- defineModel
- defineOptions
- toRef & toValue
一、支持类型导入
更新前,defineProps和defineEmits 只支持本地类型并且仅支持类型字面量和interface,不支持类型导入。
更新后,支持导入类型(import)、条件类型且可扩展类型(不支持复杂的条件类型)
1.defineProps 类型导入:
export interface HiProps {
msg: string;
}
// 导入类型定义文件
import type { HiProps } from './typings';
// defineProps 使用导入类型 + 条件类型 + 扩展类型 extends
defineProps<HIProps & true extends false ? { age: number} : {}>()
二、泛型
defineProps
和 defineComponent
可通过generic
属性接收泛型参数
1.defineProps 示例:
<script setup lang="ts" generic="T">
// 通过传入的类型推断出实际的类型
defineProps<{age: T}>()
</script>
<script setup lang="ts" generic="T, U extends string">
defineProps<{age: T, names: U[]}>()
</script>
2.defineComponent 示例:
import { defineComponent } from 'vue';
export default defineComponent(<T>(props: {msg: T}) => {
return () => <div>{props.msg}</div>;
})
<template>
<div>
<Generic msg="Hi" />
</div>
</template>
<script setup lang="ts">
import Generic from './Generic.vue'
</script>
三、defineEmits
以前,defineEmits
的类型参数仅支持调用签名语法:
const emit = defineEmits<{
(e: 'foo', id: number): void
(e: 'bar', name: string, ...rest: any[]): void
}>()
现在,在类型字面量中,key 是事件名称,value 是事件参数的数组类型
const emit = defineEmits<{
foo: [id: number];
bar: [name: string, age: number];
}>();
function handleEmitFoo(){
emit('foo', 1);
}
四、defineSlots
defineSlots() 只接受一个类型参数(类型字面量)
在类型字面量中,key 是slot 的名称,value 是slot 的函数
<template>
<div>
<slot msg="test"></slot>
<slot name="foo" :bar="1"></slot>
</div>
</template>
<script setup lang="ts">
defineSlots<{
default: (props: { msg: string }) => any;
foo: (props: { bar: number }) => any;
}>();
</script>
<template>
<div>
<Sloter>
<template v-slot="{msg}">{{ msg}}</template>
<template v-slot:foo="{bar}">{{ bar}}</template>
</Sloter>
</div>
</template>
<script setup lang="ts">
import Sloter from './Sloter.vue';
</script>
五、defineProps的解构
可以解构的 props 并保持响应性
1.首先需要在vite.config.ts 中开启配置:
export default {
plugins: [
vue({
propsDestructure: true;
})
]
}
2.使用示例:
<script setup lang="ts">
const { msg } = defineProps<HiProps>(); // HiProps类型定义在其他文件中
watchEffect(() => {
console.log(msg);
// msg是一个普通的值,编译完是props.msg,watchEffect可以收集到
})
function useTest(msg){
// msg是一个普通的值,响应式会丢失
}
</script>
<template>
<div>
<Hi :msg="msg">
<el-button @click="handleMsg">change msg</el-button>
</div>
</template>
<script setup lang="ts">
import Hi from './Hi.vue'
const msg = ref('hei');
function handleMsg(){
msg.value += 'hi'
}
</script>
六、defineModel
以前,数据的双向绑定需要两个步骤:
- 声明props
- 使用emit事件
现在,直接使用defineModel
接收就可以完成数据的双向绑定,会返回一个ref
1.首先需要在vite.config.ts 中开启配置:
export default {
plugins: [
vue({
defineModel: true;
})
]
}
2.使用示例:
<template>
<div>
<input v-model="modelValue" />
<input v-model="bar" />
</div>
</template>
<script setup lang="ts">
const modelValue = defineModel()
const bar = defineModel('bar', {
required: false,
default: 'test'
})
</script>
<template>
<div>
<DefineModel v-model="foo" v-model:bar="bar" />
</div>
</template>
<script setup lang="ts">
import DefineModel from './DefineModel.vue'
const foo = ref('123')
const bar = ref('test')
</script>
七、defineOptions
defineOptions
允许直接在<script setup>
中声明组件选项,而无需单独添加的<script>
<script setup>
defineOptions({ inheritAttrs: false })
</script>
八、toRef 和 toValue
都提供了对getter的支持
1.toRef
增强toRef
- value => ref
toRef(1); // 相当于 ref(1)
- getter => ref
toRef(() => props.msg); // 创建只读ref,使用.value时执行getter
- ref => ref
toRef(msg.value);
<script setup lang="ts">
const props = defineProps(['msg]);
function useTest(msg){
// 此处msg是一个普通的值,响应式会丢失
}
useTest(toRef(props, "msg")) ; // 使用toRef将msg 转换成ref, 解决响应式丢失问题
</script>
<script setup lang="ts">
const props = defineProps<{foo: { bar: string }}>();
function useTest(msg){
}
useTest(toRef(props.foo, "bar"));
</script>
问题:若直接修改了foo,则响应式会丢失
解决方法:
- 传入函数
useTest(() => props.foo.bar);
- 使用computed 计算属性
useTest(computed(() => props.foo.bar)) // 有缓存开销
- 使用toRef(增加,支持传入getter)
useTest(toRef(() => props.foo.bar)
)
2.toValue
- ref => value
const value1 = toValue(ref(1));
- value => value
const value2 = toValue(2);
- getter => value
const value3 = toValue(() => 3);
3.toValue 和 unref 的区别
const value3 = toValue(() => 3); // 返回的是值
const unrefValue = unref(() => 2); // 返回的是函数