组件
注册全局组件
注册全局组件需要在
main.ts中挂在组件 全局挂载 Card 组件import Card from './components/Card/index.vue'
createApp(App).component('Card',Card).mount('#app')
递归组件
<script setup lang='ts'>
import { ref, reactive } from 'vue'
import Tree from './index.vue' //递归组件需要引入自身
import type { TreeList } from '../../type/TreeList'
// type TreeList = {
// name: string,
// icon?: string,
// children?: TreeList[] | []
// }
type Props = {
data: TreeList[]
}
defineProps<Props>()
const emit = defineEmits(['putItem'])
const clickItem = (item: TreeList) => {
emit('putItem', item)
}
</script>
<template>
<div>
<!-- 点击时将点击的内容派发出去 -->
<div @click.stop="clickItem(item)" v-for="(item, index) in data" :key="index">
<div>{{ item.name }}</div>
<!-- 使用组件 -->
<!-- 使用递归时,需要让子组件也需要写派发该事件 @putItem="clickItem" -->
<!-- 派发事件名称 @putItem 派发事件的方法"clickItem" -->
<Tree @putItem="clickItem" v-if="item.children?.length" :data="item.children"></Tree>
<br/>
</div>
</div>
</template>动态组件 :is
<component :is="current.component"></component>
最小实例 (setup语法糖写法)
因为reactive本身会经过Proxy代理,组件也会去代理,为了节省性能需要使用markRaw()跳过一层代理
<script setup lang='ts'>
import { ref, reactive,markRaw } from 'vue'
import A from './A.vue'
import B from './B.vue'
import C from './C.vue'
type Tabs = {
name: string,
component: any
}
//Pick<> ts工具类,摘取 某类型 的 某字段
type Com = Pick<Tabs,'component'>
const data = reactive<Tabs[]>([
{
name: 'A组件',
component: markRaw(A) //为了节省性能需要使用markRaw()跳过一层代理
},{
name: 'B组件',
component: markRaw(B)
},{
name: 'C组件',
component: markRaw(C)
}
])
//设置一个默认的组件
let current = reactive<Com>({component: data[0].component})
//切换组件 将item.component赋值给current
const swichCom = (item: Tabs) => {
current.component = item.component
}
</script>
<template>
<div class="content">
<div class="tabs">
<!-- 点击时将item传过去 -->
<div class="tab" @click="swichCom(item)" v-for="item in data">{{ item.name }}</div>
</div>
<component :is="current.component"></component>
</div>
</template>
<style lang='less' scoped>
.content {
flex: 1;
border: 1px solid #ccc;
margin: 20px;
}
</style>插槽
组件
<script setup lang='ts'>
import { ref, reactive } from 'vue'
</script>
<template>
<div>
<div class="t"><slot></slot> <!-- 匿名插槽 --> </div>
<div class="c"><slot name="main"></slot><!-- 具名插槽 --></div>
<div class="f"><slot name="footer"></slot></div>
</div>
</template>
<style lang='less' scoped>
div>div {height: 150px;width: 100%;}
.t {background-color: antiquewhite;}
.c {background-color: forestgreen;}
.f {background-color: skyblue;}
</style>父组件使用
<script setup lang='ts'>
import Dialog from '../../components/Dialog/index.vue'
</script>
<template>
<div class="content">
<Dialog>
<template v-slot><h1>我被插入了上面</h1></template>
<template v-slot:main><h1>我被插入了中间</h1></template>
<!-- # 号简写 -->
<template #footer><h1>我被插入了下面</h1></template>
</Dialog>
</div>
</template>插槽作用域
组件
<script setup lang='ts'>
import { ref, reactive } from 'vue'
type names = {name:string,age:number}
const data = reactive<names[]>([{name:'刘德华',age:16},{name:'张三',age:26},{name:'李四',age:36}])
</script>
<template>
<div class="content"> <div class="c">
<div v-for="item in data">
<slot :data="item"></slot>
</div>
</div></div>
</template>
<style lang='less' scoped>
div>div {height: 150px;width: 100%;}
.c {background-color: forestgreen;}
</style>父组件使用
<template>
<div class="content">
<Dialog>
<template #default="{data}"><h1>{{data.name}}</h1></template>
</Dialog>
</div>
</template>动态插槽
组件
<script setup lang='ts'>
import { ref, reactive } from 'vue'
type names = {name:string,age:number}
const data = reactive<names[]>([{name:'刘德华',age:16},{name:'张三',age:26},{name:'李四',age:36}])
</script>
<template>
<div class="content">
<div class="c">
<div v-for="item in data">
<slot :data="item"></slot>
</div>
</div>
<slot name="app"></slot> <!-- name app -->
</div>
</template>
<style lang='less' scoped>
div>div {height: 150px;width: 100%;}
.t {background-color: antiquewhite;}
.c {background-color: forestgreen;}
.f {background-color: skyblue;}
</style>父组件使用
<script setup lang='ts'>
import Dialog from '../../components/Dialog/index.vue'
let name = ref('default') //name: app \ default
</script>
<template>
<div class="content">
<Dialog>
<template #[name]>
<h1>111</h1>
</template>
</Dialog>
</div>
</template>异步组件
打包时会将异步组件拆分出来
组件
<script setup lang='ts'>
import { ref, reactive } from 'vue'
import {axios} from './server'
//模拟发送请求 2000ms返回数据
const list = await axios('./data.json')
console.log(list);
</script>
<template>
<div>
{{list}}
</div>
</template>父组件使用 <Suspense> 和 defineAsyncComponent() 异步加载组件
<script setup lang='ts'>
import { ref, reactive, markRaw,defineAsyncComponent} from 'vue'
const A = defineAsyncComponent(()=>import('../../components/A/index.vue'))
let name = ref('default')
</script>
<template>
<div class="content">
<Suspense>
<template #default>
<A></A>
</template>
<template #fallback>
loading...
#fallback 可以做骨架屏,在组件未加载完毕时显示
</template>
</Suspense>
</div>
</template>Teleport传送组件
<Teleport :disabled="false" to="body"> <A/> </Teleport>
to属性可以传 ID Class等选择器,将包含的组件或内容传送到指定位置,比如 <Teleport to="body"> <A/> </Teleport>,是将 <A/>组件传送到 <body>标签内
:disabled属性可选,为flase时to属性不生效
<script setup lang='ts'>
import { ref, reactive} from 'vue'
const A = defineAsyncComponent(()=>import('../../components/A/index.vue'))
</script>
<template>
<div class="content">
<Teleport :disabled="false" to="body">
<A></A>
</Teleport>
</div>
</template>keep-alive缓存组件
<keep-alive> 标签包裹的组件会被缓存
:max="1"可以指定缓存组件的数量:include="['A']"只缓存指定组件:exclude="['A']"不缓存指定组件
搭配 v-if可以根据route.meta.keepAlive的值判断组件是否缓存
<template>
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" v-if="$route.meta.keepAlive" />
</keep-alive>
<component :is="Component" v-if="!$route.meta.keepAlive" />
</router-view>
</template>开启 keep-alive 会增加两个生命周期
onMounted(() => {console.log('走一次初始化,一次性操作')})
onActivated(()=>{console.log('keep-alive 开启')})
onDeactivated(()=>{console.log('keep-alive 卸载')})
onUnmounted(() => {console.log('不会卸载')})