vue3.3新特性,通过实例了解vue3.3新的特征

6 篇文章 0 订阅

1.开场白

5月份,vue团队发布了 vue3.3.
这次小版本的发布主要解决了–
Vue 与 TypeScript 一起使用时的许多长期存在的痛点.
下面我们一起来学习一下vue3.3新特征

2.准备新新特征的环境

根据官方团队的描述,我们需要准备一下工作。
vue升级到 3.3 时,建议同时更新以下依赖项:
Volar / vue-tsc@^1.6.4
vite@^4.3.5
@vitejs/plugin-vue@^4.2.0
vue-loader@^17.1.0 (如果使用 webpack 或 vue-cli)

3.解析导入的类型,并支持有限的复杂类型

//hello.ts文件
// 给HelloWord组件定义类型
export interface HelloPerson {
  name: string;
  age: number;
  likeArr: string[]
}
//HelloWorld.vue文件
<template>
  <div class="card">
    <p>姓名 {{ name }}</p>
    <p>年龄 {{ age }}</p>
    <p>爱好 {{ likeArr }}</p>
  </div>
</template>

<script setup lang="ts">
//导入我们定义的类型
import { HelloPerson } from './hello'
// 使用定义的类型
defineProps<HelloPerson>()
</script>
//在页面中使用
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
  <div>
    <HelloWorld name="张三" :age="19" :likeArr="['打豆豆','挖呀挖']" />
  </div>
</template>
需要注意的点
所以使用组件的时候接口中的参数必须要有,否者会有红色波浪线。
但在实际的场景中,我们有可能不需每一个参数。
为了解决这个上述问题。
我们需要将 interface 中的字段改为可选。使用 ? 还处理。
变为下面的样子就可以了。


// 给HelloWord组件定义类型
//  ? 表示该字段可以有也可以没有
export interface HelloPerson {
  name?: string;
  age?: number;
  likeArr?: string[]
}

3.1类型的扩展

有些时候我们还需要进行类型的扩展。
HelloPerson接口中没有我们需要的类型。
我们可以自己进行扩展,现在我们扩展一个其他类型
otherProp 为字符串,同样的这个类型是可有可无的
<script setup lang="ts">
//导入我们定义的类型
import { HelloPerson } from './hello'
// 使用定义的类型 同时还扩展了otherProp类型为字符串。
// 同样的这个类型是可有可无的
defineProps<HelloPerson & { otherProp?: string }>()
</script>
需要注意的点
需要注意的是,复杂类型支持是基于 AST 的,因此不是 100% 全面的。
不支持一些需要实际类型分析的复杂类型,例如条件类型。
我们可以将条件类型用于单个 props 的类型,
但不能对整个 props 对象使用。

3.2通用组件-组件可以接收泛型参数

有些时候我们不知道,传递过来的是什么类型。
这个时候我们就可以使用泛型了。
// Hi组件
<template>
  <div class="card">
    <p>爱好 {{ likeArr }}</p>
    <p>性别 {{ sex }}</p>
  </div>
</template>

<script setup lang="ts" generic="T">
defineProps<{
  likeArr: T[]
  sex: T
}>()
</script>
// 调用Hi组件
<script setup lang="ts">
import Hi from './components/Hi.vue'
let likeArr = ['摸鱼','睡觉']
</script>
<template>
  <div>
    // 在使用的时候会自动推导类型的
    <Hi :likeArr="likeArr" sex="男"/>
  </div>
</template>

3.3多个泛型

多个泛型使用逗号隔开,于此同时,也是可以继承的
// Hi组件
<template>
  <div class="card">
    <p>爱好 {{ likeArr }}</p>
    <p>性别 {{ sex }}</p>
    <p>年龄 {{ age }}</p>
  </div>
</template>

<script setup lang="ts" generic="T,U extends number">
// 让U这个泛型继承数字
defineProps<{
  likeArr: T[]
  sex: T,
  age: U
}>()
</script>
// 调用Hi组件
<script setup lang="ts">
import Hi from './components/Hi.vue'
let likeArr = ['摸鱼','睡觉']
</script>
<template>
  <div>
    <Hi :likeArr="likeArr" sex="男" :age="10" />
  </div>
</template>
<style scoped>

4.defineEmits的优化

// Say.vue
<template>
  <div class="card">
   <button @click="handlerSay"></button>
   <button @click="handlerWrite"></button>
  </div>
</template>

<script setup lang="ts" generic="T,U extends number">
const emit = defineEmits<{
  //这里的id是自定义的,你也可以叫其他
  foo: [id: number];  
  //这里的name, age是自定义的,你也可以叫其他
  bar:[name:string, age:number]
}>()
function handlerSay() {
  emit('foo',1)
}
function handlerWrite() {
  emit('bar',  '张三', 19)
}
</script>
// 调用Say.vue
<script setup lang="ts">
import Say from './components/Say.vue'
function bar(a,b) {
  console.log(a,b)
}
</script>
<template>
  <div>
    <Say @bar="bar" />
  </div>
</template>

4.1defineEmits以前的写法

<template>
  <div class="card">
   <button @click="handlerSay"></button>
   <button @click="handlerWrite"></button>
  </div>
</template>


const emit = defineEmits<{
  (e: 'foo', id: number): void
  (e: 'bar', name: string,  age: number): void
}>()

function handlerSay() {
  emit('foo',1)
}
function handlerWrite() {
  emit('bar',  '张三', 19)
}

5.使用 defineSlots 设置 slots 类型

新的 defineSlots 宏可以声明 slots 及其类型:
// Say.vue
<script setup lang="ts">
defineSlots<{
  default?: (props: { msg: string }) => any
  item?: (props: { id: number }) => any
}>()
</script>
<template>
	<slot msg="xxx" />
    <slot name="item" id="xxx"/>
</template>
// 调用Hi.vue
<script setup lang="ts">
import Say from './components/Say.vue'
function bar(a,b) {
  console.log(a,b)
}
</script>
<template>
  <div>
    <Say>
		<template #default={msg}></template>
		<template #item={id}></template>
	</Say>
  </div>
</template>


defineSlots()只接受一个类型参数,没有运行时参数。类型参数应该是一个类型字面量

key 是 slot 名称
value 是 slot 函数

函数的第一个参数是 slot 期望接收的 props,它的类型将用于模板中的 slot props。

defineSlots的返回值与 useSlots 返回的 slots 对象相同。
目前存在的限制:

volar / vue-tsc 尚未实现 slots 类型检查。
slot 函数的返回类型目前是忽略的,是任何类型,但我们可能会在将来利用它进行 slot 内容检查。

除了在 <script setup> 中使用 defineSlots 定义 slots 类型,还能在 defineComponent 中的 slots 属性中定义
typescript复制代码import { SlotsType } from 'vue'

defineComponent({
  slots: Object as SlotsType<{
    default: { foo: string; bar: number }
    item: { data: number }
  }>,
  setup(props, { slots }) {
    expectType<undefined | ((scope: { foo: string; bar: number }) => any)>(
      slots.default
    )
    expectType<undefined | ((scope: { data: number }) => any)>(slots.item)
  }
})

6.实验性功能

6.1 reactive 解构

该功能可以解构的 props 并保持响应性,并提供了一种更符合人体工程学的方式来声明 props 的默认值:

<script setup>
import { watchEffect } from 'vue'

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

watchEffect(() => {
  // 在 watch 和 computed 中使用 msg
  // 能够正常收集依赖,就好像使用 props.msg
  console.log(`msg is: ${msg}`)
})
</script>

<template>{{ msg }}</template>

此功能是实验性的,需要明确的选择加入。以 Vite 为例:
// vite.config.js
export default {
  plugins: [
    vue({
      propsDestructure: true
    })
  ]
}

6.2defineModel

以前组件想要支持 v-model,需要两个步骤:
声明 props
在打算更新 props 时,emit update:propName 事件
子组件支持 v-model 的写法:

// 子组件
<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>

或者使用computed
接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 get 和 set 函数的对象来创建一个可写的 ref 对象

// 子组件
const props = defineProps<{
	modelValue:string
}>()
const emit = defineEmits(['update:modelValue'])
const value = computed({
  get: () => props.modelValue,
  set: (val) => {
    emit('update:modeValue',val)
  }
})
<template>
  <input v-model="value" />
</template>
// 父组件
<script setup>
import { ref } from 'vue'
import Comp from './Comp.vue'

const msg = ref('')
</script>
<template>
  <Comp v-model="msg">
</template>

Vue 3.3 用新的 defineModel 宏简化了用法。宏将自动注册 props 和事件 ,并返回一个 ref:

<script setup>
const modelValue = defineModel()
console.log(modelValue.value)
</script>

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

根据接受 defineModel 返回值的变量名,这里是 modelValue,会自动定义 props 名为 modelValue,emit 事件为 update:modelValue
也支持显示传入 props 名称

const count = defineModel<number>('count', { default: 0 })

此功能是实验性的,需要明确的选择加入。

// vite.config.js
export default {
  plugins: [
    vue({
      script: {
        defineModel: true
      }
    })
  ]
}

7.其他值得注意的功能

7.1 defineOptions

//defineOptions 允许直接在 <script setup> 中声明组件选项,而无需单独的 <script> 块:
<script setup>
defineOptions({name: 'xxx', inheritAttrs: false })
</script>
//终于可以快乐地设置组件 name 了

7.2 toRef、toValue 提供 getter 支持

增强 toRef,支持将 values/getters/ref 标准化为 ref:
// 相当于 ref(1)
toRef(1)
// 创建只读 ref,使用 .value 时执行 getter
toRef(() => props.foo)
// 返回 ref
toRef(existingRef)

//调用 toRef 类似于 computed,但如果 getter 没有昂贵的计算,toRef 会更高效
//toValue 则相反,将 values/getters/ref 标准化为 value:
toValue(1) //       --> 1
toValue(ref(1)) //  --> 1
toValue(() => 1) // --> 1

8. JSX 导入源支持

目前,Vue 的类型自动注册全局 JSX 类型。这可能会与需要 JSX 类型推断的其他库一起使用时发生冲突,特别是 React
从3.3开始,Vue 支持通过 TypeScript 的 jsxImportSource 选项指定 JSX 命名空间。这允许用户根据其需要,选择全局或每个文件的选择加入。
为了向后兼容,3.3 仍然全局注册 JSX 命名空间。我们计划在 3.4 中删除默认的全局注册。如果您正在使用 TSX 与 Vue,请在升级到 3.3后在 tsconfig.json 中添加显式的 jsxImportSource,以避免在 3.4 中出现问题。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蜡笔小先

你的鼓励是我创作的最佳动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值