美文网首页
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