1. 局部组件
1.1 场景
一个页面的模块非常多,不想全写在一个页面里面,就拆成一个个局部组件,再引入使用
1.2 使用
<template>
<!-- 使用组件 -->
<head-component></head-component>
<content-component></content-component>
<foot-component></foot-component>
</template>
<script setup lang="ts">
// 引入组件(无需注册)
import HeadComponent from "..."
import ContentComponent from "..."
import FootComponent from "..."
</script>
2. 全局组件
2.1 场景
一个组件在全局范围内使用频率非常高,可以在全局引入并注册这个组件,在任何地方都能直接使用
2.2 全局单独引入并注册
// main.ts
// 引入
import GlobalComponentVue from '...'
// 注册
const app = create(App)
app.component('GlobalComponent', GlobalComponentVue) // 参数为 组件名称 和 组件
2.3 全局批量引入并注册(以 Element-UI 为例)
// main.ts
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = create(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
3. 递归组件
3.1 场景
比如树和多级菜单,就是递归出来的
3.2 使用
// App.vue
<template>
<!-- 将数据传给子组件 -->
<tree :data="data"></tree>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
import Tree from './components/Tree.vue'
// 定义一个数据类型
interface Menu {
name: string,
checked: boolean,
children?: Menu[]
}
// Mock 一些数据
const data = reactive<Menu[]>([
{
name: '1',
checked: true,
},
{
name: '2',
checked: true,
children: [
{
name: '2-1',
checked: false
},
{
name: '2-2',
checked: true
}
]
},
{
name: '3',
checked: false,
children: [
{
name: '3-1',
checked: true,
children: [
{
name: '3-1-1',
checked: false
}
]
}
]
},
])
</script>
// Tree.vue
<template>
<div v-for="item in data" :key="item.name" style="margin: 10px">
<input type="checkbox" v-model="item.checked"/> <span> {{ item.name }} </span>
<tree v-if="item?.children?.length" :data="item?.children"></tree>
</div>
</template>
<script setup lang="ts">
// 在子组件中还是要定义同个数据类型,用于对接收数据的类型判定
interface Menu {
name: string,
checked: boolean,
children?: Menu[]
}
const { data } = defineProps<{
data: Menu[] // 声明接收的数据类型
}>()
</script>
页面效果 ↓
如果递归时,组件名不想使用文件名,想更改组件的名称怎么办?
方式一:在递归组件中,加一个 script 标签(注意不要加 setup 属性),内部默认暴露一个对象,属性为一个 name
<script lang="ts">
export default { name: "OwnName" }
</script>
方式二:使用插件 `unplugin-vue-define-options`
// 第一步:安装插件 npm i unplugin-vue-define-options
// 第二步:vite.config.ts 引入并注册
import DefineOptions from 'unplugin-vue-define-options/vite'
export default defineConfig({
plugins: [DefineOptions()],
})
// 第三步:tsconfig.json 配置 types
{
"compilerOptions": {
"types": ["unplugin-vue-define-options/macros-global"]
}
}
// 第四步:递归组件中使用 defineOptions
defineOptions({
name: "OwnName"
})
3.3 绑定事件
<template>
<!-- 绑定事件,并阻止冒泡,$event 是事件对象 -->
<div @click.stop="clickTap(item, $event)" v-for="item in data" :key="item.name" class="menu">
<input type="checkbox" v-model="item.checked"/> <span> {{ item.name }} </span>
<tree v-if="item?.children?.length" :data="item?.children"></tree>
</div>
</template>
<script setup lang="ts">
interface Menu {
name: string,
checked: boolean,
children?: Menu[]
}
const { data } = defineProps<{
data: Menu[]
}>()
const clickTap = (item: Menu, e: any) => {
console.log(item)
console.log(e.target) // 获取事件源对象
}