有些情况第三方的UI组件库不能满足我们自己的需求,需要定制一些功能上去,有没有一种可能,vue也可以写像写面向对象语言一样,继承一个组件,然后定制一些功能呢
以下案例基于vue3实现

例如我现在有一个组件NButton,现在想给这个组件加上一个权限判断,当登录用户没有此按钮的权限时,将对此按钮不可见或禁用等

extends

文档地址:https://cn.vuejs.org/api/options-composition.html#extends

详细信息:
使一个组件可以继承另一个组件的组件选项。

我们通过extends: NButton继承一个组件库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// YButton.vue

<script lang="ts">
import { defineComponent } from 'vue'
import { NButton } from "naive-ui"

export default defineComponent({
extends: NButton,
setup: NButton.setup,
props: {
permission: [String]
},
mounted() {
// 当 permission 有没有 resource:add 权限时不显示此按钮
if (this.permission !== "resource:add") {
this.$el.style.display = "none";
}
}
});
</script>

在home文件中导入此组件

1
2
3
4
5
6
7
8
9
10
// Home.vue
<template>
<div>
<YButton type="success" permission="resource:add">按钮</YButton>
</div>
</template>

<script setup lang="ts">
import YButton from "@/components/YButton.vue"
</script>

./10-12-Vue3组件继承/2022-10-13-12-09-31.png

可以看到我们按钮自定义的按钮显示出来了

这时我们把permission删掉试试,按钮就被隐藏了,这时也达到了开头提到的需求

1
<YButton type="success">按钮</YButton>

但是此方法在只能继承某个组件,不能对组件加入一些其他dom元素,万一我还需要加个其他标签怎么办呢
且此方法因为获取父组件的setup,所以在ts中,语法检查会报错,望大佬解释以下
./10-12-Vue3组件继承/2022-10-13-12-17-54.png

透传 Attributes

文档地址:https://cn.vuejs.org/guide/components/attrs.html

“透传 attribute”指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的 attribute 或者 v-on 事件监听器。最常见的例子就是 class、style 和 id。

深层组件继承

文档地址:https://cn.vuejs.org/guide/components/attrs.html#nested-component-inheritance

透传的 attribute 不会包含 上声明过的 props 或是针对 emits 声明事件的 v-on 侦听函数,换句话说,声明过的 props 和侦听函数被 “消费”了。

以上面的作为例子,简单说就是,当一个NButton作为根组件时,绑定在YButton上的 attribute 会直接传给里面的NButton
下面我们用这种方式进行编码

1
2
3
4
5
6
7
8
9
10
11
<template>
<n-button><slot></slot></n-button>
</template>

<script>
import { defineComponent } from 'vue';

export default defineComponent({

});
</script>

可以看到我们的按钮正常显示了,然后我们再加入我们的权限验证

1
2
3
4
5
6
7
8
9
10
export default defineComponent({
props: {
permission: String
},
mounted() {
if (this.$props.permission !== "resource:add") {
this.$el.style.display = "none"
}
}
});

但是这时仍还有一个问题就是,目前我们只能挂载默认插槽,具名插槽没有显示出来,我们可以用循环$slots来插入进去,

1
2
3
<n-button>
<slot v-for="(s, name) in $slots" :name="name"></slot>
</n-button>

添加其他html标签

文档地址:禁用 Attributes 继承

因为 template 中的根标签或默认继承 Attributes,所以我们可以使用inheritAttrs选项来禁用继承

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// YButton.vue

<template>
<div v-if="$props.permission === 'resource:add'">
<n-button v-bind="$attrs">
<slot v-for="(s, name) in $slots" :name="name"></slot>
</n-button>
我是新增的元素
</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
inheritAttrs: false,
props: {
permission: String
},
mounted() {

}
});
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Home.vue

<YButton type="success" permission="resource:add">
<template #icon>
<n-icon>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512 512">
<path d="M256 48C141.31 48 48 141.32 48 256c0 114.86 93.14 208 208 208c114.69 0 208-93.31 208-208c0-114.87-93.13-208-208-208zm0 313a94 94 0 0 1 0-188h4.21l-14.11-14.1a14 14 0 0 1 19.8-19.8l40 40a14 14 0 0 1 0 19.8l-40 40a14 14 0 0 1-19.8-19.8l18-18c-2.38-.1-5.1-.1-8.1-.1a66 66 0 1 0 66 66a14 14 0 0 1 28 0a94.11 94.11 0 0 1-94 94z" fill="currentColor"/>
</svg>
</n-icon>
</template>
刷新
</YButton>

./10-12-Vue3组件继承/2022-10-13-13-20-30.png