首页 > 其他分享 >Vue3.3 的新功能的体验(下):泛型组件(Generic Component) 与 defineSlots

Vue3.3 的新功能的体验(下):泛型组件(Generic Component) 与 defineSlots

时间:2023-05-26 12:22:05浏览次数:57  
标签:类型 name Generic 插槽 Component defineSlots 组件 props 泛型

上一篇说了 DefineOptions、defineModel、Props 的响应式解构和从外部导入类型 这几个新功能,但是没有说Generic、defineSlots等,这是因为还没有完全搞清楚可以用在什么地方。折腾了几天终于弄清楚了。

这还要从 TS 的泛型说起。

泛型的目的和意义

泛型仅仅只是表达传啥都行吗?当然不是,因为js原生就支持“泛型”,本来就啥都可以传的。
泛型的目的是——约束!泛型相当于制定了一个白名单,名单里面的类型可以传,不在名单里面的不可以传。

TS 的泛型可以帮助我们更准确的推断类型,从而在编写代码的时候,可以有更准确的提示和提供验证依据。

泛型组件(Generic Component)

组件的props可以设置各种类型,那么如果想用泛型的话,要如何设置呢?这就需要使用 Generic:

<script setup lang="ts" generic="T extends {name: string} ">
   
  const props = defineProps<{
    list: T[], // 泛型的方式
    list2: number[], // 只能是 number 类型的数组
    list3: Array<any>, // 任意类型的数组
    name: string,
    person: {
      name: string
    }
  }>()

  console.log('props-ts:\n', props)

这里定义了几个属性,第一个使用了泛型,第二个是 number[],第三个是任意类型的数组。

我们来看看不同类型的提示信息:

  • Array<any> 提示的时候,无法获知具体的类型。
    100any.jpg

  • number[] 必须和设置的类型完全一致。
    101number.jpg

  • T[] 可以根据传入的类型做出对应的提示

    • 传入 {name: string}
      102T1.jpg

    • 传入 {name: string, age: number}
      103T2.jpg

    • 类型不匹配的提示

104Terr.jpg

对比一下,我们可以发现,使用泛型可以准确的推断类型,在模板里面可以有更准确的提示,如果类型不合格,可以有提示信息。
这样在编写代码的时候可以避免低级错误。

defineSlots

defineSlots 是做什么的呢,是定义插槽还是获取插槽?准确的说,是定义作用域插槽props的类型(支持泛型),然后返回父组件传入的插槽。

在 setup 里面定义插槽的类型

在组件里面定义两个插槽,一个是匿名插槽,一个是作用域插槽(col),

定义一个 list 的属性,传入一个数组,然后遍历这个数组,创建一组列表,列表内使用作用域插槽。

通过作用域插槽的props把数组元素传递给父组件:(好像有点绕)

<script setup lang="ts" generic="T extends Object ">
  
  const props = defineProps<{
    list: T[], // 泛型的方式
  }>()

  const slot = defineSlots<{
    default(props: any): any,
    col(props:
      {
        row: T,
        index: number
      }): any
  }>()
  console.log('slot:\n', slot)
<template>
  <!--匿名插槽-->
  <slot></slot>
  <div v-for="(item, index) in list" :key="index">
    <!--作用域插槽-->
    <slot name="col" :row="item" :index="index" ></slot>
  </div>
</template>

父组件里使用的方法

<script setup lang="ts">
  import { reactive } from 'vue'
  // 加载子组件
  import ts from './20-ts.vue'
  // 定义数组
  const list2 = reactive([
    {
      name: '11',
      age: 10
    },
    {
      name: '66',
      age: 10
    }
  ])
</script>
<template>
  <ts :list="list2" > <!--传入数据列表-->
    <h1>测试插槽</h1>
    <template #col="{ item, index }"> <!--用解构的方式获取-->
      序号:{{ index }}<br> <!--其实这里是循环-->
      内容:{{ item }}
    </template>
  </ts>
</template>

UI库里的 table 组件一般都会支持这样的插槽,以便于灵活设置列表,比如 el-table 的 el-table-column:

(来自官网示例代码)

<el-table :data="tableData" style="width: 100%">
    <el-table-column label="日期" width="180">
      <template #default="scope">
        <div style="display: flex; align-items: center">
          <el-icon><timer /></el-icon>
          <span style="margin-left: 10px">{{ scope.row.date }}</span>
        </div>
      </template>
    </el-table-column>
    ...
  </el-table>

这里的 default 就是一个匿名作用域插槽,可以通过scope.row获得每一行的数据。

defineEmits

defineEmits 是定义事件的一种快捷表达方式,也是一种语法糖,这个和 defineModel 有重合的地方,那就是 v-model 的 update:modelValue 的部分。

话说,组件需要事件吗?以前是事件驱动,现在是数据驱动,或者说是状态驱动。以前监听事件,现在只需要监听状态的变化即可,从dom脱离出来。

好吧,其实我基本已经不使用 emit 了,感觉似乎并不需要了。

参考资料

Generic component enhancements - Discussion #436:

unplugin-vue-define-options - npm: https://www.npmjs.com/package/unplugin-vue-define-options

Announcing Vue 3.3 | The Vue Point: https://blog.vuejs.org/posts/vue-3-3

Vue 3.3 主要新特性详解 - 三咲智子 Kevin Deng: https://xlog.sxzz.moe/vue-3-3

5 Vue3.3 发布:十分钟速递

6 官方帮助文档

7 elementPlus

标签:类型,name,Generic,插槽,Component,defineSlots,组件,props,泛型
From: https://www.cnblogs.com/jyk/p/17434408.html

相关文章

  • Deferred Components-实现Flutter运行时动态下发Dart代码 | 京东云技术团队
    导读DeferredComponents,官方实现的Flutter代码动态下发的方案。本文主要介绍官方方案的实现细节,探索在国内环境下使用DeferredComponents,并且实现了最小验证demo。读罢本文,你就可以实现Dart文件级别代码的动态下发。一、引言DeferredComponents是Flutter2.2推出的功能,依赖于......
  • 两个视图基类GenericAPIView,APIView
    基于两个视图基类GenericAPIView,APIViewclassBookserializers(serializers.ModelSerializer):#name=serializers.CharField()#price=serializers.CharField()#publish_id=serializers.CharField(write_only=True)#Authors=serializers.ListFie......
  • [React Typescript] Overriding and Removing Component Props
    UsingOmitimport{ComponentProps}from'react';import{Equal,Expect}from'../helpers/type-utils';exportconstInput=(props:Omit<ComponentProps<'input'>,'onChange'>&{onChange:......
  • vue component:is 组件切换
    <template><Child1/><Child2/><component:is="currentComp"></component><el-button@click="compChange">切换组件</el-button></template><scriptsetup>import{shallo......
  • Vue项目报错: Component name “xxx“ should always be multi-word vue/multi-word-co
    报错的意思是组件名应该始终是多单词,不应该以单个单词命名组件解决办法1:修改组件名称:例如当前的登陆组件名是login.vue修改成LoginName.vue,组件名需要以驼峰式命名至少两个单词,不一定都得是LoginName.vue可以是NameLogin.vue也可以是LoginNiu.vue总之就是以驼峰式命名......
  • 【键值-对象池】GenericKeyedObjectPool
    目录GenericKeyedObjectPool1.依赖2.配置3.连接对象类4.对象池工厂5.使用GenericKeyedObjectPool​ 通用池化框架commons-pool2实践,其中提到了可以池化一个对象和一组对象,一个对象用到了GenericObjectPool这个类,一组对象用到了GenericKeyedObjectPool这个类。顾名思义,键值......
  • Component name "tag" should always be multi-word.
    Componentname"tag"shouldalwaysbemulti-word.这种错误通常是由于编码规范或代码风格指南中的命名规范没有被遵守所导致的。vue中,我命名一个组件叫tag好像不符合规范,应该怎么命名?在Vue中,如果你要命名一个组件,可以采用驼峰式命名法,即使用多个单词组成的名称,每个单词的首......
  • 关于error The "ApexChart" component has been registered but not used 问题的解决
    问题描述学习了vue之后,但是还没熟练使用的我,发现删除某些模块会使得整个界面报错,真的是又被无语到(被自己哈!)问题解决仔细看了看这个报错,发现是因为这个界面定义了一些vue模块,但是由于我的修改,导致它们被定义之后,并没有得到相应的调用;然后解决的话,就很简单,将我们对这些模块再这......
  • You may have an infinite update loop in a component render function
    在组件渲染函数中你可能有一个无限更新的循环这就导致页面一直在加载无限循环下去,没有终止,卡死 在 v-for 循环当中,如果用方法或者计算属性对vm.$data的属性进行操作,理论上,可能因为修改到循环对象,诱发无限循环。此时Vue就会发出警告(并不是真的已经无限循环了) ......
  • 碎片化学习前端之HTML(webComponent)
    前言webComponent是HTML5推出的新特性,为组件化推广奠定基础。webComponent基本使用原生组件,性能较好,但存在兼容性问题。其核心技术有:Customelements,ShadowDOM,HTMLTemplates。CustomelementsJavaScriptAPI,用于定义customelements及其行为。<m-buttontype="p......