Vue3组件相关知识

当我们去开发一个项目时,出现频率比较高的一个组件我们可以把他抽离为一个==全局组件==,直接在其他的组件上使用。当我们在一个页面上模块非常多,我们可以把每个模块当做一个==局部组件==,在一个页面内引入使用。==递归组件==就是我们需要重复的在一个组件内复用自己,比如像是菜单或者是树形结构,就是通过递归得到的结构。

同时我们也可以在某个组件中动态的切换其他的组件,来达到v-if的效果。

局部组件

我们定义的每一个Vue文件就是一个组件, 在使用局部组件时直接引入就可以直接使用。

全局组件

注册全局组件的方式

1
2
3
4
5
6
7
8
9
// main.ts

import Example from 'xxx/xxx/index.vue'

// 第一个参数:组件名,可以任意定义,此处和组件实例同名
// 第二个参数:引入的组件实例
app.component('Example', Example)

app.mount('#app')

使用的方式:

直接在其他的任意组件中使用 <Example></Example>

多个组件可以参考element-plus的 icon 组件的批量注册方式:

1
2
3
4
5
6
7
8
9
// main.ts

// 如果您正在使用CDN引入,请删除下面一行。
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}

递归组件

在使用递归组件时第个一条件就是确定调用组件名称,第一种方式就是直接使用当前的文件名当做组件名。

父组件数据:

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
import { ref } from 'vue'

const data = ref([
{
name: "1",
check: true,
},
{
name: "2",
check: false,
children: [
{
name: "2-1",
check: true,
}
]
},
{
name: "3",
check: false,
children: [
{
name: "3-1",
check: true,
children: [
{
name: "3-1-1",
check: true,
},
{
name: "3-1-2",
check: false,
}
]
}
]
},
])

子组件递归调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script setup lang=ts>
import { ref } from 'vue';
defineProps(['data'])

</script>

<template>
<div v-for="item in data">
<input type="checkbox" v-model="item.check">
<span>{{ item.name }}</span>
<Tree v-if="item?.children?.length" :data="item?.children"></Tree>
</div>
</template>

<style scoped lang='scss'></style>

如果不使用自身的文件名当做递归的组件名称, 还可以在重新开启一个新的script标签,但是不能写 setup。

例如:

1
2
3
4
5
<script lang="ts">
export default {
name: "componentName"
}
</script>

在组件中使用可以直接使用componentName, <componentName></componentName>

弊端就是还需要再重新写一个script的标签, 很繁琐。

另一种方式就是需要安装一个插件:unplugin-vue-define-options 然后在需要一通设置。

在使用递归组件时,使用点击事件会有一个事件冒泡的问题,所以如果使用事件的话要添加@click.stop=eventHandler。 接受参数可以使用$event

动态组件

先说一下使用场景, 比如我们在做一些 tab页切换时,可以使用路由或者是v-if或者是动态组件

直接上示例代码

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<script setup lang=ts>
import { ref, reactive } from 'vue';
import ACom from './components/A.vue'
import BCom from './components/B.vue'
import CCom from './components/C.vue'

// 待切换的组件数组
const componentsArr = reactive([
{
name: "A",
com: ACom
},
{
name: "B",
com: BCom
},
{
name: "C",
com: CCom
}
])

// 绑定的动态组件
const activeCom = ref(ACom)

// tab的选中样式类
const activate = ref(0)

// 切换事件
const switchHandler = (item, index) => {
activate.value = index
activeCom.value = item.com
}
</script>

<template>
<div class="box">
<div class="tab">
<div @click="switchHandler(item, index)" class="tab-item" :class="activate == index ? 'activate' : ''"
v-for="(item, index) in componentsArr">
<div>{{ item.name }}</div>
</div>
</div>
<component :is="activeCom"></component>
</div>
</template>

<style scoped lang='scss'>
.activate {
color: white;
background-color: green;
}

.box {
display: flex;
flex-direction: column;
margin-left: 10px;
margin-top: 10px;

.tab {
display: flex;

.tab-item {
cursor: pointer;
width: 100px;
height: 50px;
margin-right: 10px;
border: 1px solid gray;
display: flex;
align-items: center;
justify-content: center;
}
}
}
</style>

但是会有一个问题,vue会抛出一个警告:

大概是因为vue对切换的组件中的对象也做了一个代理,但是我们这个场景没有必要在做一层代理 所以可以使用 markRaw, shallowRef 来做一个性能优化, 来避免再次代理。

最终的代码为

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<script setup lang=ts>
import { ref, reactive, markRaw, shallowRef } from 'vue';
import ACom from './components/A.vue'
import BCom from './components/B.vue'
import CCom from './components/C.vue'

const componentsArr = reactive([
{
name: "A",
com: markRaw(ACom)
},
{
name: "B",
com: markRaw(BCom)
},
{
name: "C",
com: markRaw(CCom)
}
])

// 绑定的动态组件
const activeCom = shallowRef(ACom)

// tab的选中样式类
const activate = ref(0)

// 切换事件
const switchHandler = (item, index) => {
activate.value = index
activeCom.value = item.com
}
</script>

<template>
<div class="box">
<div class="tab">
<div @click="switchHandler(item, index)" class="tab-item" :class="activate == index ? 'activate' : ''"
v-for="(item, index) in componentsArr">
<div>{{ item.name }}</div>
</div>
</div>
<component :is="activeCom"></component>
</div>
</template>

<style scoped lang='scss'>
.activate {
color: white;
background-color: green;
}

.box {
display: flex;
flex-direction: column;
margin-left: 10px;
margin-top: 10px;

.tab {
display: flex;

.tab-item {
cursor: pointer;
width: 100px;
height: 50px;
margin-right: 10px;
border: 1px solid gray;
display: flex;
align-items: center;
justify-content: center;
}
}
}
</style>

具体的优化方式可以看下这个满神的这个视屏