Vue3.3等类库最新特性及其使用

一、Vue3.3最新特性

Vue 3.3 “Rurouni Kenshin”——浪客剑心的最新版本主要目标是优化开发者的使用体验,包括引入一些新的简化语法和宏,以及在TypeScript方面的进一步提升。

以下是主要的更新内容:

1.为setup语法糖组件引入了泛型功能;
2.允许在组件文件中导入外部的ts类型;
3.对defineEmits语法进行了优化;
4.新增defineSlots定义插槽类型;
5.新增defineModel来简化modelValue的语法;
6.Reactive Props 解构
7.新增defineOptions定义组件名称以及其他一些配置项;
8.对toRef和toValue的功能进行了增强。

接下来对这些改动一一介绍

1.setup语法糖泛型

使用<script setup>的组件现在可以通过generic属性接受泛型类型参数,一般情况下用不到,但有时候组件比较复杂时无法推断类型的时候非常有用

<script setup lang="ts" generic="T">
import {defineProps} from "vue";
const props =  defineProps<{
  items: T[];
  selected: T;
}>();
</script>

2.支持导入外部ts类型

defineProps和defineEmits支持使用import外部导入的类型声明。

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

// 使用导入的类型 + 交集类型(导入类型基础上增加一个字段)
defineProps< People & { extraProp?: string }>() //在vue3.3之前不支持使用import导入的类型
</script>

generic 的值与 TypeScript 中 <...> 之间的参数列表用法完全相同。例如,您可以使用多个参数、extend 约束、默认类型和引用导入的类型:

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

3.defineEmits语法优化

之前defineEmits的类型参数只支持函数调用签名语法:

// 以前
const emit = defineEmits<{
  (e: 'foo', id: number): void
  (e: 'bar', name: string, ...rest: any[]): void
}>()
// 或者不定义类型
const emit = defineEmits(['update:modelValue'])

在vue3.3中可以简化为以下写法,更加简洁

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

在类型字面量中,key 是事件名称,value 是事件参数的数组类型。
以前的函数调用签名语法仍然受支持。

4.新增defineSlots

新的defineSlots宏可以用来声明插槽的类型,例如:

子组件DefineSlots

<script setup lang="ts">
defineSlots<{
  default?: (props: { msg: string }) => any
  item?: (props: { id: number }) => any
}>()
</script>

其中,defineSlots中的item就是我们定义的插槽名称,props就是我们定义的插槽的参数,any就是我们定义插槽的返回值,msg、id就是插槽的参数。

父组件

<template>
  <DefineSlots>
    <template #default="{msg}" >{{msg}}</template>
    <template #item="{id}" >{{id}}</template>
  </DefineSlots>
</template>

<script setup lang="ts">
import DefineSlots from './components/defineSlots.vue';
</script>

插槽函数的返回类型目前被忽略。

试验性功能:

5.新增defineModel

用于简化自定义v-model双向绑定语法,在vue3.3中此功能是实验性的,需要明确的选择加入。

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

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

子组件支持 v-model 的写法:

<template>
  <input :value="modelValue" @input="onInput" />
</template>

<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

function onInput(e) {
  emit('update:modelValue', e.target.value)
}
</script>

简化后的写法

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

<script setup>
const modelValue = defineModel()
// 也可以直接修改,等价于emit('update:modelValue', '新值')
// modelValue.value = '新的值'
</script>

6.Reactive Props 解构

该功能可以解构的 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.config.js
export default {
  plugins: [
    vue({
      propsDestructure: true
    })
  ]
}

其它值得注意的功能:

7.新增 defineOptions

新的defineOptions宏允许直接在<script setup>中声明组件选项,而不需要单独的<script>块

当我们想使用 mounted钩子函数时,会报错,因为 <script setup>会将所有的代码都放在 setup函数中,而 mounted是在 setup函数之后执行的,所以会报错。
此外某些场景缓存页面数据,可能需要设置组件名称 name。

可以用 defineOptions 定义任意选项,但 props, emits, expose, slots 除外

<script setup>
import { onMounted } from 'vue';
// eslint-disable-next-line no-undef
defineOptions({
  name: 'DefineOptions',
  inheritAttrs: false,
  mounted() {
    console.log('mounted');
  }
})
onMounted(()=>{
  console.log('onMounted')
})
</script>

8.toRef和toValue增强

toRef已得到增强,将元素变成响应式,支持将值/getter/现有refs规范化为refs:

// 等价于ref(1)
toRef(1)
// 创建一个readonly ref,在.value访问时调用getter 
toRef(() => props.foo)
// 按原样返回现有的引用
toRef(existingRef)

使用getter调用toRef类似于computed,但当getter只是执行属性访问而没有昂贵的计算时,效率会更高。

新的toValue实用程序方法提供了相反的功能,将values / getters / refs标准化为值:

toValue(1) //       --> 1
toValue(ref(1)) //  --> 1
toValue(() => 1) // --> 1

toValue可以在composable中代替unref使用,这样你的composable就可以接受getter作为反应式数据源:

// 以前:分配不必要的中间引用
useFeature(computed(() => props.foo))
useFeature(toRef(props, 'foo'))

// 现在:更高效和简洁
useFeature(() => props.foo)

toRef和toValue之间的关系类似于ref和unref之间的关系,主要区别在于对getter函数的特殊处理。

9.依赖更新

依赖更新
升级到 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)

二、Vue Router新特性

在使用vue-router4中params 进行路由组件之间传参

<script setup lang="ts">
import { useRouter } from 'vue-router'

const router = useRouter()
const params = { id: '1', name: 'wjj', phone: 123456789, age: 23 }
function toFirst() {
  router.push({ name: 'first', params })
}
</script>

在接收页面尝试渲染params传递的数据:

template>
  <div>姓名:{{ route.params?.name }}</div>
  <div>电话:{{ route.params?.phone }}</div>
  <div>年龄:{{ route.params?.age }}</div>
</template>

<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
</script>

跳转页面接收不了并出现如下Vue Router警告:


警告

我们点击连接后发现了原因:


github更新日志

也就是说,从Vue Router的2022-8-22 这次更新后,我们使用上面的方式在新页面无法获取。

Vue也给我们提出了代替方案:

1.使用query的方式传参

修改为query方式传参数,注意query方式只能用路由表中的path,不是name,并且所有的参数都会显示在URL地址上。

<script setup lang="ts">
import { useRouter } from 'vue-router'

const router = useRouter()
const params = { id: '1', name: 'wjj', phone: 123456789, age: 23 }
function toFirst() {
  // query方式
  router.push({ path: '/first', query: params })
}
</script>
跳转之后参数显示在URL上

2.将数据放在pinia或者vuex这样的状态管理库里面

实际工作中,咱们非必要不会使用这种方式。

3.使用动态路由匹配

如果传递参数较少的情况下,可以尝试使用动态路由匹配方式,只要修改一下path定义部分就可以了:

{
      // path: '/first',
      path: '/first/:id/:name/:phone/:age', // 动态路由,修改一下path定义
      name: 'first',
      component: () => import('../views/FirstView.vue')
    }

跳转页面通过path,接收页面使用route.params

<script setup lang="ts">
import { useRouter } from 'vue-router'

const router = useRouter()
function toFirst() {
  // 动态路由
  router.push({ path: '/first/1/wjj/123456789/23'})
}
</script>
<!--动态路由方式-->
<p>姓名:{{ route.params?.name }}</p>
<p>电话:{{ route.params?.phone }}</p>
<p>年龄:{{ route.params?.age }}</p>

注意:如果使用了动态路由匹配方式,path中的每个参数都是必须传递,否则会报错。
警告

个人觉得动态路由匹配方式接收参数与params方式一样,如果不把params参数写在路由路径上无法得到params参数,虽然不算弃用了params,但是每次都把params参数都写在路由路径上也是非常麻烦的一件事。

4.使用HistoryAPI的方式

在跳转页面使用state参数

<script setup lang="ts">
import { useRouter } from 'vue-router'

const router = useRouter()
const params = { id: '1', name: 'wjj', phone: 123456789, age: 23 }
function toFirst() {
  // state
  router.push({ name: 'first', state: { params } })
}
</script>

跳转后的页面接收:

<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
const historyParams = history.state.params
</script>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容