vue组件封装技巧

[TOC]

vue组件封装技巧

1.props参数

最简单父传子的技巧,在父组件传入值,子组件声明props接收就可以了

如下:

父组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div id="app">
<Table content="外面调用的"></Table>
</div>
</template>

<script>
import Table from './components/Table.vue';
export default {
name: 'App',
components: {
Table
}
}
</script>

子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>{{content}}</div>
</template>
<script>
export default {
data() {
return {
}
},
props:['content'],
mounted() {
console.log("父亲过来的",this.$attrs);
},
}
</script>

上面是用的props的数组语法,下面是对象语法,做更近一步处理时可以使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,

// 多个可能的类型
propB: [String, Number],

// 必填的字符串
propC: {
type: String,
required: true,
},

// 带有默认值的数字
propD: {
type: Number,
default: 100,
},

// 具有默认值的数组写法
propK: {
type: Array,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return ["张三"];
},
},

// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: "hello" };
},
},

// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ["success", "warning", "danger"].indexOf(value) !== -1;
},
},
},

2.$attrs

用途:在对组件进行二次封装的时候会出现孙子组件的传值问题,如果都通过prop进行层层传递太过麻烦,可以选择不使用props传值而通过$attrs传递

定义:包含了父作用域中不被认为 (且不预期为) props 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 props 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件——在创建更高层次的组件时非常有用。

用法:父组件正常传入数据,子组件通过在孙组件上v-bind=:”$attrs”将未props声明的数据传递,孙组件通过this.$atrs.xxx进行使用

父组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div id="app">
<Table content="外面调用的" msg="这是测试attrs"></Table>
</div>
</template>

<script>
import Table from './components/Table.vue';
export default {
name: 'App',
components: {
Table
}
}
</script>

子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div v-bind="$attrs">{{this.$attrs.content}}</div>
</template>

<script>
export default {
data() {
return {};
},

mounted() {
console.log("父亲过来的", this.$attrs);
}
};
</script>

打印台信息:

image-20221014135456615

但这有一个需要注意的点:$attrs将propos未声明的属性会作为HTML属性绑定到组件的根元素上

这时候需要添加:

1
inheritAttrs:false

image-20221014141754671

3.$emit

可以用于子组件向父组件传递消息,触发定义的事件

父组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div >
<last @msgA="msgA"></last>
</div>
</template>

<script>
import last from '@/components/last.vue'
export default {
data() {
return {};
},
components:{
last
},
methods:{
msgA(name){
console.log("事件被调用了",name);
}
}
};
</script>

子组件:

1
2
3
4
5
6
7
8
9
10
11
<template>
<div></div>
</template>

<script>
export default {
created(){
this.$emit('msgA','sssss')
},
}
</script>

可以看到控制台输出的内容

image-20221019142634683

4.$listeners

与 $attrs的用法类似,当我们需要将事件处理函数传递给子组件时,可以利用父组件实例上的 $listeners属性,这个属性包含了组件接收到的事件处理函数,需要在子组件上绑定:v-on=”$listeners”

父组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div >
<last @msgA="msgA"></last>
</div>

</template>

<script>
import last from '@/components/last.vue'
export default {
data() {
return {};
},
components:{
last
},
methods:{
msgA(name){
console.log("事件被调用了",name);
}
}
};
</script>

子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div v-bind="$attrs" v-on="$listeners"></div>
</template>

<script>
export default {
created(){
this.$emit('msgA','sssss')

},
mounted() {
console.log("父亲过来的值", this.$attrs);
console.log("父亲传过来的事件",this.$listeners);
this.$listeners.msgA('sdddddddd')
},

}
</script>

可以看到控制台打印的信息

image-20221019143754249

简单来说:$attrs与$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners里存放的是父组件中绑定的非原生事件。

5.插槽

一个通用组件,往往不能够适应所有应用场景,所以在封装组件的时候只需要完成组件 80% 的功能,剩下的 20% 让父组件通过 solt 解决。比如:某一个公共组件中有两个按钮,一个是“新增”,一个是“删除”,但是在另外的场景中使用这个组件时,两个按钮需要做的事情是不一样的,比如是“查看”和“修改”。所以,我们在封装组件的时候就不要直接写按钮了,而是在合适的位置放置一个slot,其实是一个占位的作用,给按钮的设置提前预留一个位置,然后在父组件中写入按钮即可。
具体插槽如何使用我之前发过一篇文章,见下方链接
插槽


vue组件封装技巧
https://tian-1-2.github.io/typblog/2022/10/13/vue组件封装技巧/
作者
田云鹏
发布于
2022年10月13日
许可协议