泛型在强类型多态组件中的应用
复制粘贴一时爽,一直粘贴垃圾场。那一坨坨垃圾迟早得自己收拾,除非你比香港记者跑得还快。
在实际的业务中,动态的数据类型和动态组件以及组件的扩展并不鲜见。比如你有多个不同类型的数据,你希望以相同的方式展示;或者你在运行时才能确定期望渲染哪个组件;又或者你想扩展你的组件,使之行为根据不同的数据类型动态化。
这时候,多态组件或许是你的最佳方案。多态组件是一种把一个组件的多种形态抽象为一个单一组件的技术。这样,当需要扩展功能时,只需要向该组件中添加新的形态即可。
一个简单的多态组件类似这样:
在这个例子中,cmp调用时可根据不同场景传入各自想要的tagName和对应的props。
下一步,你期望后续的代码有关于类型的智能提示。比如tagName限定为原生html标签,当cmp被调用,tagName传值字符串abc,你期望得到一个错误提示:abc不是某某某的属性;props各个属性映射为对应的element各个属性,随便什么阿猫阿狗也会有错误提示。
这就需要为tagName分配你期望的多种类型,并且在tagName和props之间建立约束关系。泛型正是为此而生。(如果你对泛型有所了解,或熟知泛型的优势,请往下划过N个代码片段直接看结论。)
TypeScript 中的泛型是用来解决类型不确定的问题的特殊语法。泛型允许我们在定义一个函数、类或接口时不指定具体类型,并在使用这个函数、类或接口时指定具体类型。
一个简单的泛型函数🌰:
一个灵活的多态组件如果添加了泛型,定义时便无需指定具体类型,使用时可以根据分配的类型得到期望的结果。
假设现在需要为html标签及其属性创建约束。HTMLElementTagNameMap 是typescript内建类型,源码类似这样:
定义所有的标签类型和标签对应的Element对象:
给组件添加泛型约束:
Vue 提供了一个 h()
函数用于创建 vnodes。这是一种约定俗成的实现虚拟DOM的方式,用JavaScript来生成html标记语言。它的第一参数是必填的,可以是html标签,也可以是自定义组件。接下来我们用 h( ) 完善这个具有强类型约束的动态组件。
我们来写几个例子试试看这个类型约束是否生效:

根据vue3官网文档:
component 是一个用于渲染动态组件或元素的“元组件”。要渲染的实际组件由
is
prop 决定。is的值可以是字符串(htmlTag或者组件的注册名称),也可以直接绑定到组件。
在vue组件中的应用,我们将 is 直接绑定到 Input 组件:
看下效果:

通过以上步骤,我们初步实现了一个具有类型约束的多态组件,支持传入html标签和属性以便动态渲染,并且能在使用时得到期望的错误提示和结果。
在实际业务中,一个很常见的场景是我们往往还需要传入自定义组件或开源组件库的组件。接下来,让我们来扩展一下。
有两个解决办法,如果非得写箭头函数的话:
也可以这样活用extends:
如果你的函数式组件泛型刚好需要extends 某个类型,这种方式可以说是恰到好处恰如其分的。
当然,还可以不写箭头函数: