久别重逢,最近换了新工作,三个月很忙碌,以后博客会定期更新;前几天Vue发布了3.3,我今年没写过Vue,但是也关注Vue的进展,总的来说Vue的发展方向在version 3这个版本中基本稳定了,反而DX的设计上会越来越好,如果你不使用Typescript,那么Vue3的许多新特性你可能并不适用,今天我们简单看一下Vue3.3的内容。

概览 (GPT总结)

  • defineOptions:可以在 setup 中使用它来声明组件名称等组件属性,避免了需要再写一个script标签,使得新手和 Vue2 -> Vue3 的用户更容易理解。
  • setup 中的 TypeScript 类型改进:支持从其他类型文件中导入类型,不再需要在 内使用 defineProps 等宏定义类型,但是目前还不支持复杂的类型操作。
  • defineModel:简化了父组件和子组件双向通信(v-model)的代码,使得双向绑定的逻辑更加明了。
  • defineSlots:可以自定义组件的 slot 类型,适用于在复杂场景下 Volar 不能准确推导组件类型的情况。
  • 泛型组件:对于带有 slot 的复杂组件来说,可以自动推断传入的类型。
  • setup 中的提升变量:支持将基础数据类型(除了 Symbol)的变量提升到顶部,解决了之前在 defineProps 时只能使用字面量的问题。

defineOptions

在之前的setup语法糖里面,如果要声明组件名称等组件属性,需要再写一个script标签,这会给新手和Vue2 -> Vue3的用户带来困惑,并且会给Eslint/Volar带来一定的难度,现在可以使用defineOptions宏来指定这样的信息,但是defineOptions不允许指定props/emits,因为这两者可以使用其他宏指定。

<script setup lang="ts">
defineOptions({
  name: 'HelloWorld',
})
</script>

setup中的Typescript类型改进

这是一个很旧的问题,在使用defineProps时,是不能从其他地方引入类型使用的,可以具体查看RFC。在这个版本将支持从其他类型文件中导入,比如这样:

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

defineProps<Props & { extraProp?: string }>()
</script>

只不过目前并不支持复杂的类型操作,在之前我们使用defineProps做类型声明时,如果传入了类型,编译器将会将类型转换为运行时代码;而现在如果要支持外部的类型要么就调用Typescript的龟速编译器,要么就自己实现一个基础的编译器,让其识别到导入的“是什么类型”。目前Vue采用了第二种方案,并且支持一个复杂类型的例子(如上代码所示)。

defineModel

简化了父组件和子组件双向通信(v-model)的代码,比如在以前我们编写v-model相关逻辑,需要写这么多代码:

<script setup lang="ts">
const props = defineProps<{
  modelValue: number
}>()

const emit = defineEmits<{
  (evt: 'update:modelValue', value: number): void
}>()

emit('update:modelValue', props.modelValue + 1)
</script>

现在使用defineModel:

<script setup>
const modelValue = defineModel<number>()
modelValue.value++
</script>

defineSlots

在复杂场景下,有时候Volar并不能准确推导组件类型,或者有时你想自定义组件的slot类型,可以使用defineSlots:

defineSlots<{
  default(props: { item: T }): any
}>()
<HelloWorld :data="['foo', 'bar']">
  <template #default="{ item }">{{ item }}</template>
</HelloWorld>

泛型组件

这对于普通组件来说意义不大,但是对于带有slot的复杂组件来说很有用,意味着可以自动推断传入的类型,在组件中使用,比如以下的场景:

<script setup lang="ts" generic="T extends string | number, U extends Item">
import type { Item } from './types'
defineProps<{
  id: T
  list: U[]
}>()
</script>

T和U都可以被用作emit/ts代码逻辑中,它和defineSlot在一起用最好

scripts中的提升变量

Vue3在之前在编译sfc的template时,会将静态内容提升,这样在大型项目中重复的静态内容会用这样的优化手段会得到提升,可以看一下之前的Vue文章。但是现在在setup中,也支持这样的优化了,如果在script中编写了基础数据类型(除了Symbol)的变量,都会被提升到顶部。这个改进主要解决了之前的问题,比如defineProps时,只能使用字面量:

<script setup>
const hello = 'world'
defineOptions({
  hello,
})
</script>

这样的代码在之前是会有编译错误的,现在不会了,原因是defineProps宏编译之后,会变成下面的代码:

const __sfc__ = {
  props: [propName],
  setup(__props) {
    
  },
}

显然这是错误的,无法访问到propName,经过提升优化之后,将会解决这个问题:

const __sfc__ = {
  propName: "hello",
  props: [this.propName],
  setup(__props) {
    
  },
}

(伪代码)

更符合人体工学 的 defineEmits

同样的,这次改进主要是少写一些代码:

const emit = defineEmits<{
  (e: 'foo', id: number): void
  (e: 'bar', name: string, ...rest: any[]): void
}>()

现在可以这么写:

const emit = defineEmits<{
  foo: [id: number]
  bar: [name: string, ...rest: any[]]
}>()

解构Props不失去其响应式(实验性)

如下代码所示,这更好的提供props的默认值:

const { msg = 'hello' } = defineProps(['msg'])

此时,msg仍然还是一个响应式变量,并且如下代码可能也不会再需要了:

export interface Props {
  msg?: string
  labels?: string[]
}

const props = withDefaults(defineProps<Props>(), {
  msg: 'hello',
  labels: () => ['one', 'two']
})

在此前经常使用withDefaults宏去提供props默认值,现在有更好的趋近于es6的写法,所以建议大家之后可以使用这个功能,但是这个功能是实验性质的,需要自行斟酌。

标签: none

添加新评论