最近 vue 发布了 3.3 版本,新增了不少宏和语法糖以及改进了一些过往的写法以提高开发者的体验,下面带领大家体验一下 Vue3.3 版本。本篇文章将使用 vite 进行演示。
注意,当我们使用npm create vite
的时候 vue 默认的版本还是 3.2 版本,所以我们需要手动将 vue 以及其它改成如下版本在进行 install
安装完毕我们便可以体验最新 3.3.4 版本了
宏函数 defineOptions
或许有的同学在做需要为组件命名的时候见过这个函数,但是是通过unplugin-vue-define-options
插件实现的,而在 3.3 版本中它已经被纳入了 Vue 中
现在开发 vue3 项目基本都是用的 setup 语法的形式,这种形式在以前如果想要给组件命名则还需要写一个 script 标签或者使用插件,譬如
- 额外的 script 标签
js
<script>
export default {
name: 'MyComponent',
}
</script>
<script setup>
...
</script>
这种显然不是很美观
- 使用插件
unplugin-vue-define-options
js
//vite.config.ts
import DefineOptions from 'unplugin-vue-define-options/vite'
export default defineConfig({
plugins: [
DefineOptions(),
]
})
//setup 中
<script setup>
defineOptions({
name: 'mycomponent'
})
</script>
而在 3.2 中则可以直接使用,当然传入的属性也不止 name 一种,比如有在 setup 中不能使用的beforeCreate
生命周期等
js
defineOptions({
name: "mycomponent",
beforeCreate() {
console.log(123);
},
});
便捷的 defineEmits
在 3.3 之前使用 defineEmits 向父组件发送一个 sendMsg 事件的时候写法如下
js
defineEmits<{
(e: 'sendMsg', value: string): void
}>()
第一个参数代表事件名,后面的参数表示传递的事件传递的参数,看起来不是很直观,所有 vue3.3 增加了语法糖
js
defineEmits<{
'sendMsg': [value:string]
}>()
提高类型导入体验
在 3.3 之前组件类型的引入只能引入本地的类型,不同从其它文件中导入,比如如下写法在 3.3 之前是不支持的,而在 vue3.3 版本得到了支持
js
import type { Props } from './types'
defineProps<Props>()
并且还支持了一些复杂的类型,比如你想让组件接收一个额外的类型 name 你可以这样写
js
defineProps<Props & { name?: string }>()
泛型组件 Generic
vue3.3 中泛型组件主要是用来约束组件接收的参数,比如想接收一个 name 为 string 类型的对象,它的写法如下
js
<script lang='ts' setup generic="T extends { name:string }">
defineProps<T>()
</script>
reactive 解构
这个功能可以让我们结构 props 并且不会失去响应式,比如
js
<script lang='ts' setup generic="T extends { name:string }">
const { name } = defineProps<T>()
</script>
其中的 name 变量依然是响应式的。 这个功能是实验性 的,如果要使用需要在vite.config.ts
中配置
js
export default {
plugins: [
vue({
propsDestructure: true,
}),
],
};
defineModel
在以前如果想要组件支持 v-model,我们需要这样写
- 子组件
先声明 props,当想要更新 props 的时候,emit 一个update:modelValue
事件
js
<template>
<div @click="sendMsg">按钮</div>
</template>
<script lang='ts' setup>
const props = defineProps<{ modelValue: string }>()
const emit = defineEmits<{ (e: 'update:modelValue', value: string): void }>()
const sendMsg = () => emit('update:modelValue', '222')
</script>
- 父组件
v-model='msg'
其实是:modelValue='msg' @update:modelValue='msg=$event'
的语法糖,此时我们点击按钮 msg 就会更新为 222
js
<template>
<div>
<Comp v-model="msg" />
</div>
<div>{{ msg }}</div>
</template>
<script lang='ts' setup>
import Comp from './App2.vue'
import { ref } from 'vue';
const msg = ref('')
</script>
这种写法是不是很麻烦,而使用了 defineModel 宏就会变得很简单,只需要定义一个 modelValue 即可
- 子组件
js
<template>
<div @click="sendMsg">点击</div>
</template>
<script lang='ts' setup>
const modelValue = defineModel()
const sendMsg = () => {
modelValue.value = '222'
}
</script>
这样就能实现同样的功能,同样的 defineModel 也是一个实验性的功能,如果想用它则需要在vite.config.ts
中进行定义
js
export default defineConfig({
plugins: [
vue({
script: {
defineModel: true,
},
}),
],
});
defineSlots
defineSlots 可以定义一个插槽的类型,比如子组件
js
<template>
<div>
<div>
<slot name="a" foo="yueyue" :bar="1" />
</div>
<div>
<slot name="b" :option="{ name: '小月', age: 18 }" />
</div>
</div>
</template>
<script lang='ts' setup>
defineSlots<{
a(props: { foo: string; bar: number }): any,
b(props: { option: { name: string, age: number, sex?: string } }): any
}>()
</script>
它包含了两个插槽分别为 a 和 b,同时我们用defineSlots
给他们定义了类型,然后我们可以在父组件中使用
js
<template>
<div>
<Comp>
<template #a="{ foo, bar }">
{{ foo }} is string,{{ bar }} is number
</template>
<template #b="{ option }">
{{ option.name }}
</template>
</Comp>
</div>
</template>
<script lang='ts' setup>
import Comp from './App2.vue'
</script>
有了 defineSlots 用户就能更好地确定自己该怎么用某个 scopeSlot 了。
除了这些 Vue3.3 还做了性能的优化和一些小的改动这里就不再列举,更多细节大家可以点击这里查看