Skip to content

组件

注册全局组件

注册全局组件需要在 main.ts 中挂在组件 全局挂载 Card 组件 import Card from './components/Card/index.vue'

createApp(App).component('Card',Card).mount('#app')

递归组件

html
<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()跳过一层代理

html
<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>

插槽

组件

html
<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>

父组件使用

html
<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>

插槽作用域

组件

html
<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>

父组件使用

html
<template>
  <div class="content">
    <Dialog>
      <template #default="{data}"><h1>{{data.name}}</h1></template>
    </Dialog>
  </div>
</template>

动态插槽

组件

html
<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>

父组件使用

html
<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>

异步组件

打包时会将异步组件拆分出来

组件

html
<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() 异步加载组件

html
<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属性不生效

html
<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的值判断组件是否缓存

html
<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('不会卸载')})