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

上一篇说了 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 了,感觉似乎并不需要了。

参考资料

1 Generic component enhancements - Discussion #436:

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

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

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

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

6 官方帮助文档

7 elementPlus

本文转载于网络 如有侵权请联系删除

相关文章

  • Python 回调函数实现异步处理

    作者|无量测试之道 编辑|小晴这是无量测试之道的第158篇原创说到异步处理大家应该会联想到Ajax处理,那我们先来说说什么是Ajax请求。 Ajax就相当于是模拟了一个信息发送请求,你可以在很多网站上注册的时候会发现,比如用户名输入“123”,那么它可能会提示你该用户已经存在,而给你的感觉是页面并没刷新,也就是并没有提交表单,而用户名又是存放在数据库内的,也就是说要查询用户名是否存在,就必须得发送表单的里的用户名,然后再在数据库中去查询。而这个过程就是用了Ajax来处理的,用户输入用户名,当表单的焦点发生变化的时候,则会触发Ajax,然后Ajax发送一个GET或者POST请求给服务器,服务器就会处理传递过来的数据!今天给大家分享的是在Python里面通过回调函数来实现异步的处理。示例代码如下所示:importthreading importtime importdatetime #第一个请求 defrequest_1(): print("therequest1isstart") io(callback) print("therequest1isend&q

  • 对不起,我错了,这代码不好写

    hello,大家好呀,我是小楼。前几天不是写了这篇文章《发现一个开源项目优化点,点进来就是你的了》嘛。文章介绍了Sentinl的自适应缓存时间戳算法,从原理到实现都手把手解读了,而且还发现Sentinel-Go还未实现这个自适应算法,于是我就觉得,这简单啊,把Java代码翻译成Go不就可以混个PR?甚至在文章初稿中把这个描述为:「有手就可以」,感觉不太妥当,后来被我删掉了。过了几天,我想去看看有没有人看了我的文章真的去提了个PR,发现仍然是没有,心想,可能是大家太忙(懒)了吧。于是准备自己来实现一遍,周末我拿出电脑试着写一下这段代码,结果被当头一棒敲醒,原来这代码不好写啊。如何实现先简单介绍一下我当时是如何实现的。首先,定义了系统的四种状态:const( UNINITIALIZED=iota IDLE PREPARE RUNNING )复制这里为了让代码更加贴近Go的习惯,用了iota。用了4种状态,第一个状态UNINITIALIZED是Java版里没有的,因为Java在系统初始化时默认就启动了定时缓存时间戳线程。但Go版本不是这样的,它有个开关,当开关开启时,会调用Start

  • ★ Android ExpandableListView中子元素无法点击 解决方案!

    这几天公司写个电商项目,写道购物车页面,发现ExpandableListView点击子类的方法无效!!解决方法 【1】首先检查购物车中的Adapter中isChildSelectable方法是否为true@Override publicbooleanisChildSelectable(intgroupPosition,intchildPosition){ returntrue; }复制【2】查看子类View中是否有EdittextCheckBox这类抢夺焦点的控件,(我这里就有个CheckBox)如果是Edittext记得加上Edittext的父布局上加上android:focusable="true" android:focusableInTouchMode="true"复制 【3】如果View中带有抢夺焦点的控件使用android:focusable=“true”以然没有效果,那就检查一下看是哪个Viewlickable为true!去掉即可【4】这里我要说的就是它了,我出现的问题就是前三个方法都试验了,可结果依然不好用!在子类的View布局的

  • MySQL 亿级数据导入导出/数据迁移笔记

    大数据技术与架构点击右侧关注,大数据开发领域最强公众号!大数据点击右侧关注,大数据真好玩!本文是一个一个MySQL亿级数据的迁移记录,趁此记录下学习笔记。数据迁移,工作原理和技术支持数据导出、BI报表之类的相似,差异较大的地方是导入和导出数据量区别,一般报表数据量不会超过几百万,而做数据迁移,如果是互联网企业经常会涉及到千万级、亿级以上的数据量。导入和导出是两个过程,即使做数据迁移我们也要分开来看,同时,导入/导出方式又分为:•MySQL自带导入/导出方式•各类客户端导入/导出方式先总结下导出:1、导出对于字段较少/字段内容较少的数据,通过客户端方式可以采用navicat等工具导出,我这里本次导出三个字段,都是11位数字以内的值,用navicat导出每分钟大约250万数据,2、MySQL自带的导出语句:selectintooutfile语句;复制这里fields之前很简单都可看懂,不做说明,讲下fields之后的:•FIELDSTERMINATEDBY','代表我字段和字段之间用逗号分开,如:字段A字段B,导出时候显示格式为:A,B•OPTIONALLYENCLOS

  • php基础教程 第六步 学习数组以及条件判断switch补充

    条件语句switch在上一节的学习中,学习了php的条件语句if。在php编程中进行条件判断还可以使用switch语句。switch语句语法如下:<?php switch(值或表达式) { case值等于值1: 当值等于值1时要执行的代码 break; case值等于值2: 当值等于值2时要执行的代码 break; default: 当值都不等于以上条件值时要执行的代码 } ?>复制代码示例如下:<?php $a=12; switch($a) { case8: echo'$a==8'; break; case12: echo'$a==12'; break; default: echo'$a==?'; } ?>复制以上示例中,定义了一个变量a,值为12,使用switch条件语句,把变量a作为条件,放置在switch后的圆括号中。在switch大括号中,使用关键字case进行判断。case8表示如果变量a的值等于8,那么则执行case8:冒号后,以及下一个case或者default前的代码。 如果变量a的值

  • 【C++简明教程】找数组或者Vector中最大最小值的索引

    导言今天带来的程序是找出数组或者Vector中最大最小值的索引在Python中,我们可以使用numpy库快速实现,那接下来就看看C++是怎么实现的吧主要使用到的函数是max_element和min_element基本用法如下,分为数组和vector:max_element(arr,arr+arr_length)//arr是数组,arr_length是数组长度 max_element(v.begin(),v.end())//v是vector数据结构 复制基本程序#include<iostream> #include<vector> #include<algorithm> usingnamespacestd; intmain(intargc,char**argv){ floatarr[]={1.0,2.0,3.5,6.7,1.22,0.77,90.0,36.11}; intarr_length=sizeof(arr)/sizeof(arr[0]);//数组长度 //max_element(arr,arr+arr_length)计算出来是一个地址,我

  • 语义分割 | 轻量级实时分割经典BiSeNet及其进化

    01轻量级语义分割基于轻量化网络模型的设计作为一个热门的研究方法,许多研究者都在运算量、参数量和精度之间寻找平衡,希望使用尽量少的运算量和参数量的同时获得较高的模型精度。目前,轻量级模型主要有SqueezeNet、MobileNet系列和ShuffleNet系列等,这些模型在图像分类领域取得了不错的效果,可以作为基本的主干网络应用于语义分割任务当中。然而,在语义分割领域,由于需要对输入图片进行逐像素的分类,运算量很大。通常,为了减少语义分割所产生的计算量,通常而言有两种方式:减小图片大小和降低模型复杂度。减小图片大小可以最直接地减少运算量,但是图像会丢失掉大量的细节从而影响精度。降低模型复杂度则会导致模型的特征提取能力减弱,从而影响分割精度。所以,如何在语义分割任务中应用轻量级模型,兼顾实时性和精度性能具有相当大的挑战性。02经典之作:BiseNet论文地址:https://arxiv.org/abs/1808.00897.pdf代码地址:https://github.com/CoinCheung/BiSeNet本文对之前的实时性语义分割算法进行了总结,发现当前主要有三种加速方法:1)

  • 数据结构与算法 1-1 算法引入

    本系列是我在学习《基于Python的数据结构》时候的笔记。本小节主要介绍什么是数据结构与算法并通过一个问题来引出算法,算法本质就是解决问题的思路。一什么是数据结构与算法?如果将最终写好运行的程序比作战场,码农便是指挥作战的将军,而我们所写的代码便是士兵和兵器。那么数据结构和算法就是~兵法! 如果没有兵法的话,不使用战略,可能会胜利也可能会失败。但是即便胜利,可能也会付出巨大的代价。但是如果有了兵法,可以大大的提高取胜的概率。类似的,如果我们在写代码的时候没有数据结构与算法的思想的话,照样能够写出代码,解决相应的任务,但是可能会面临很多问题,比如:如果没有看过数据结构与算法,在遇到一个问题的时候可能没有任何的思路,不知该如何下手去解决;如果没有看过数据结构与算法,虽然大部分时间可能解决了问题,可是对于程序运行效率和开销没有意识,导致程序的性能低下;如果没有看过数据结构与算法,在借助别人开发利器应用到自身的问题当中,遇到性能瓶颈的时候,不知道该如何进行针对性的优化。因此,数据结构和算法是一名程序开发人员的必备基本功,不是一朝一夕就能练成绝世高手的。冰冻三尺非一日之寒,需要我们平时不断的主动

  • Spring Boot 2.x实战之StateMachine

    SpringStateMachine是一个状态机框架,在Spring框架项目中,开发者可以通过简单的配置就能获得一个业务状态机,而不需要自己去管理状态机的定义、初始化等过程。今天这篇文章,我们通过一个案例学习下SpringStateMachine框架的用法。案例介绍假设在一个业务系统中,有这样一个对象,它有三个状态:草稿、待发布、发布完成,针对这三个状态的业务动作也比较简单,分别是:上线、发布、回滚。该业务状态机如下图所示。img实战接下来,基于上面的业务状态机进行SpringStateMachine的演示。创建一个基础的SpringBoot工程,在主pom文件中加入SpringStateMachine的依赖:<?xmlversion="1.0"encoding="UTF-8"?> <projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xs

  • 黑客组织“隐匿者”技术升级再次暴力威胁入侵全网用户

    前言:区块链安全咨询公司 曲速未来 表示:"隐匿者"最早出现在2014年,此后一直从事入侵服务器或者个人主机的黑色产业,他们通过植入后门程序控制这些设备(肉鸡),然后进行DDoS攻击,也会将这些肉鸡出租给其他黑产团伙,近期,则主要利用这些"肉鸡"来"挖矿"——生产比特币。首先是经过对大量的病毒攻击事件深入研究,挖掘出了一个作恶累累的黑客犯罪团伙,并将其命名为"隐匿者",该团伙可能由中国人组成或参与(下面会说明)。这可以说是近年来互联网上最活跃、发起攻击次数最多、攻击范围最广的黑客团伙,拥有非常强的技术能力,并完全以牟利为目的。之后通过对"隐匿者"攻击相关样本字符串特征的整理,就发现"隐匿者"相关样本中均出现了中文调试信息。如下图所示:图1.f4321.com、mykings.pw和mys2016.info域名中具有地域特征的字符串信图2.mykings.top和oo000oo.club域名中具有地域特征的字符串信息根据上述信息,大概就可以推测"隐匿者&quo

  • DataTable导入到Excel文件

    public static bool DataTableToExcel(System.Data.DataTable dt, string fileName, bool showFileDialog=false)         {             if (showFileDialog)             {                 SaveFileDialog saveFileDialog = new SaveFileDialog();                 saveFileDialog.Filter = "Execl files (*.xls)|*.xls";                 saveFileDialog.FilterIndex = 0;                 saveFileDialog.RestoreDirectory = true;                 saveFileDialog.CreatePrompt = true;                 saveFileDialog.Fi

  • Leetcode 290. Word Pattern

    Givena pattern andastring str,findif str followsthesamepattern.Here follow meansafullmatch,suchthatthereisabijectionbetweenaletterin pattern anda non-empty wordin str.Examples: pattern= "abba",str= "dogcatcatdog" shouldreturntrue.pattern= "abba",str= "dogcatcatfish" shouldreturnfalse.pattern= "aaaa",str= "dogcatcatdog" shouldreturnfalse.pattern= "abba",str= "dogdogdogdog" shouldreturnfalse.Notes: Youmayassume pattern

  • 腾讯云容器安全服务统计容器逃逸各事件类型和待处理事件数api接口

    1.接口描述接口请求域名:tcss.tencentcloudapi.com。 统计容器逃逸各事件类型和待处理事件数 默认接口请求频率限制:20次/秒。 APIExplorer提供了在线调用、签名验证、SDK代码生成和快速检索接口等能力。您可查看每次调用的请求内容和返回结果以及自动生成SDK调用示例。 2.输入参数以下请求参数列表仅列出了接口请求参数和部分公共参数,完整公共参数列表见公共请求参数。 参数名称 必选 类型 描述 Action 是 String 公共参数,本接口取值:DescribeEscapeEventTypeSummary。 Version 是 String 公共参数,本接口取值:2020-11-01。 Region 否 String 公共参数,本接口不需要传递此参数。 3.输出参数 参数名称 类型 描述 ContainerEscapeEventCount Integer 容器逃逸事件数 ProcessPrivilegeEventCount Integer 程序提权事件数 RiskContainerEventC

  • Treiber Stack介绍

    简介 TreiberStack在R.KentTreiber在1986年的论文SystemsProgramming:CopingwithParallelism中首次出现。它是一种无锁并发栈,其无锁的特性是基于CAS原子操作实现的。 实现 下面给出的Java语言实现为《Java并发编程实战》一书的15.4.1小结中的实现。TreiberStack的实现套路很简单,就是CAS+重试,不需要任何注释就能轻松的看懂代码。 @ThreadSafe publicclassConcurrentStack<E>{ AtomicReference<Node<E>>top=newAtomicReference<Node<E>>(); publicvoidpush(Eitem){ Node<E>newHead=newNode<E>(item); Node<E>oldHead; do{ oldHead=top.get(); newHead.next=oldHead; }while(!top.compareAndSe

  • C# -- 继承规则

    例子1--C#继承的常见问题: usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; usingSystem.Threading.Tasks; namespaceConsoleApplication1 { classProgram { staticvoidMain(string[]args) { Animalanimal=newAnimal(); animal.Introduce(); Console.WriteLine(animal.word); //父类指向子类是可以的 Animalcat=newCat(); cat.Introduce(); Console.WriteLine(cat.word); //子类指向父类才是不能的所以下面代码将会报错不能说“父亲像儿子” //Catcat2=newAnimal(); //cat2.Introduce(); //Console.WriteLine(cat2.word); //Dog类中没有重写父类的虚方法所以调用的是父类

  • C# 多线程同步和线程通信

    多线程通信 1.当线程之间有先后的依赖关系时,属于线程之间的通信问题。也就是后一个线程要等待别的一个或多个线程全部完成,才能开始下一步的工作。可以使用: WaitHandleClass  WaitHandle类作为基类来使用的,它允许多个等待操作。这个类封装了win32的同步处理方法。WaitHandle对象通知其他的线程它需要对资源排他性的访问,其他的线程必须等待,直到WaitHandle不再使用资源和等待句柄没有被使用。下面是从它继承来的几个类:    AutoResetEvent:Notifiesawaitingthreadthataneventhasoccurred.Thisclasscannotbeinherited.这个类可以通知一个或多个线程发生事件。当一个等待线程得到释放时,它将状态转换为signaled。用set方法使它的实例状态变为signaled。但是一旦等待的线程被通知时间变为signaled,它的转台将自动的变为nonsignaled。如果没有线程侦听事件,转台将保持为signaled。此类不能被继承。 http://msdn.microsoft.com/en

  • Codeforces 786C Till I Collapse

    看到题目第一反应:出题人好善良,出个1e5放两个log过 然后真的过了 (๑• . •๑) 题解: 首先每次贪心地找最远能延伸到的地方肯定是对的——显然法可知,多拿走一个对后面的选取只有有利的影响 这里有一个简单的证明:n+n/2+n/3+...+n/n=O(nlogn)——不要问我怎么证,我真的不会(话说这个应该是常识吧) 然后只要兹磁logn查询一个只有i个颜色的区间就可以了 开一棵主席树记录从[i,n]的点中有贡献的位置,每次查询一个最后的位置k使a1+a2+...+ak≤i即可=>也就是第i+1个1的位置-1 考虑这棵主席树如何维护:从尾往前跑,每次只会有1个点(新增点)出现新的贡献,最多1个点(最近的同色点)的贡献被抹杀 没了 1#include<bits/stdc++.h> 2#definemid(l+r>>1) 3usingnamespacestd; 4intN,n,tem; 5introot[5000000],tr[5000000],ls[5000000],rs[5000000],a[5000000],last[50

  • 在django项目中再开启一个socket服务

    在django项目中再开启一个socket服务 1.任意项目任意地方写一个createSocket.py文件(文件名任意) importsocket fromdjango.confimportsettings sock=socket.socket() defstartSocket(): ip=settings.LOCALHOSTIP port=settings.LOCALHOSTPORT sock.bind((ip,port)) sock.listen(10) whileTrue: try: conn,ip=sock.accept() res=conn.recv(1024) print(res) except: continue 复制 2.在manage.py中开启进程,运行socket #!/usr/bin/envpython """Django'scommand-lineutilityforadministrativetasks.""" importos importsys frommultiprocessingimportProcess fromsmartlabdemo.so

  • TopHat

    WhatisTopHat? TopHatisaprogramthatalignsRNA-Seqreadstoagenomeinordertoidentifyexon-exonsplicejunctions.Itisbuiltontheultrafastshortreadmappingprogram Bowtie.TopHatrunson Linux and OSX. HowdoesTopHatfindjunctions? TopHatcanfindsplicejunctionswithoutareferenceannotation.ByfirstmappingRNA-Seqreadstothegenome,TopHatidentifiespotentialexons,sincemanyRNA-Seqreadswillcontiguouslyaligntothegenome.Usingthisinitialmappinginformation,TopHatbuildsadatabaseofpossiblesplicejunctionsandthen

  • Adobe全家桶PS、PR、AU等2022正版永久有效,无需破解直接安装就能用

    【Adobe全家桶】已经亲测绝对好用,下载地址: 关注我的wx公众号“奋斗在IT”回复1013获取下载地址。 更多内容及Java+大数据个人原创视频,可关注公众号观看: 原创文章,转载请注明出处!!

  • rabbitMQ学习(二)

    一端发送,多端消费   发送端: importjava.io.IOException; importcom.rabbitmq.client.ConnectionFactory; importcom.rabbitmq.client.Connection; importcom.rabbitmq.client.Channel; importcom.rabbitmq.client.MessageProperties; publicclassNewTask{ privatestaticfinalStringTASK_QUEUE_NAME="task_queue"; publicstaticvoidmain(String[]argv) throwsjava.io.IOException{ ConnectionFactoryfactory=newConnectionFactory(); factory.setHost("localhost"); Connectionconnection=factory.newConnection(); Channelchannel=connec

相关推荐

推荐阅读