美文网首页
Vue3.3等类库最新特性及其使用

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

作者: Joymerry | 来源:发表于2023-09-22 09:08 被阅读0次

一、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>

相关文章

网友评论

      本文标题:Vue3.3等类库最新特性及其使用

      本文链接:https://www.haomeiwen.com/subject/uleavdtx.html