Bootstrap

学习vue3四 (全局,局部,递归,动态组件 和 插槽)

目录

配置全局组件

配置局部组件

配置递归组件

动态组件

插槽Sort

匿名插槽

具名插槽

作用域插槽

动态插槽


配置全局组件

例如组件使用频率非常高(table,Input,button,等)这些组件 几乎每个页面都在使用便可以封装成全局组件

mian.ts中

import { createApp } from 'vue'
import App from './App.vue'
import Card from "./components/Card.vue";

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

要放到mount的前面

使用时

<Card></Card>即可

配置局部组件

就是在一个组件内(A) 通过import 去引入别的组件(B) 称之为局部组件

应为B组件只能在A组件内使用 所以是局部组件

如果C组件想用B组件 就需要C组件也手动import 引入 B 组件

案例,在递归组件内有提到

配置递归组件

也就是在template里面有引用了一便自己,和js的递归差不多

案例

子组件

<script setup lang="ts">
type TreeData = {
  name: string;
  active: boolean;
  children?: TreeData[];
}
defineProps<{
  data: TreeData[]
}>()
const handleClick = (item: TreeData) => {
  console.log(item)
}
</script>

<template>
  <div
      @click.stop="handleClick"
      v-for="item in data" :key="item.name"
      style="margin-left: 10px;">
    <input type="checkbox" v-model="item.active" />
    <span>{{ item.name }}</span>
    <BaseRefAndReactive v-if="item?.children?.length" :data="item.children" />
  </div>
</template>

<style scoped>

</style>

如果想换一个名字,可以再写一个script标签

<script lang="ts">
export default {
    name: 'Base'
}
</script>

这样就可以使用Base这个组件名来进行递归了

父组件

<script setup lang="ts">
import BaseRefAndReactive from "./components/BaseRefAndReactive.vue";
import {ref} from "vue";
type TreeData = {
  name: string;
  active: boolean;
  children?: TreeData[];
}
const treeData = ref<TreeData[]>([
  {
    name: "Node 1",
    active: false,
    children: [
      {
        name: "Node 1-1",
        active: false,
        children: [
          {
            name: "Node 1-1-1",
            active: false,
          },
          {
            name: "Node 1-1-2",
            active: false,
          },
        ],
      },
      {
        name: "Node 1-2",
        active: false,
      },
    ],
  },
  {
    name: "Node 2",
    active: false,
    children:[]
  }
])
</script>

<template>
  <Card></Card>
 <BaseRefAndReactive :data="treeData"></BaseRefAndReactive>
</template>

<style scoped>

</style>

动态组件

让多个组件使用同一个挂载点,并动态切换,这就是动态组件。

在挂载点使用component标签,然后使用v-bind:is=”组件”

案例:写一个tab,有三个按钮点击不同的按钮,切换不同的组件

<script setup lang="ts">
import A from "./components/A.vue";
import B from "./components/B.vue";
import Card from "./components/Card.vue";
import {ref,reactive} from "vue";
const comData = reactive([
  {
    name:"A",
    com: A
  },
  {
    name:"B",
    com: B
  },
  {
    name:"C",
    com: Card
  }
])
const com = ref(comData[0].com)
const active = ref(0)
function changeView(index:number){
  active.value = index
  com.value = comData[index].com
}
</script>

<template>
  <div class="container">
    <div
        v-for="(item,index) in comData" :key="index"
        :class="{'active':active === index}"
        @click="changeView(index)">
      {{item.name}}
    </div>
  </div>
  <component :is="com"></component>
</template>

<style scoped>
.container {
  width: 500px;
  display: flex;
  justify-content: center;
  margin: 0 auto;
}
.container div {
  margin: 10px;
  cursor: pointer;
  width: 100px;
  height: 30px;
  line-height: 30px;
  border: 1px solid grey;
  text-align: center;
}
.active {
    background-color: skyblue;
    color: white;
}
</style>

这样的话我们就完成了动态组件,但是这样写的会有一些警告

原因是:我们将组件放到了reactive和ref中对他进行了响应式包装,造成了一些性能浪费我们可以进行修改推荐我们使用shallowRef 或者  markRaw 跳过proxy 代理

如下:

<script setup lang="ts">
import A from "./components/A.vue";
import B from "./components/B.vue";
import Card from "./components/Card.vue";
import {shallowRef,ref,reactive,markRaw} from "vue";
const comData = reactive([
  {
    name:"A",
    com: markRaw(A)
  },
  {
    name:"B",
    com: markRaw(B)
  },
  {
    name:"C",
    com: markRaw(Card)
  }
])
const com = shallowRef(comData[0].com)
const active = ref(0)
function changeView(index:number){
  active.value = index
  com.value = comData[index].com
}
</script>

<template>
  <div class="container">
    <div
        v-for="(item,index) in comData" :key="index"
        :class="{'active':active === index}"
        @click="changeView(index)">
      {{item.name}}
    </div>
  </div>
  <component :is="com"></component>
</template>

<style scoped>
.container {
  width: 500px;
  display: flex;
  justify-content: center;
  margin: 0 auto;
}
.container div {
  margin: 10px;
  cursor: pointer;
  width: 100px;
  height: 30px;
  line-height: 30px;
  border: 1px solid grey;
  text-align: center;
}
.active {
    background-color: skyblue;
    color: white;
}
</style>

 这样就没有警告了

插槽Sort

插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot> 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的<slot></slot>标签。

匿名插槽

父组件内
<A>
  <template v-slot:default>
    <p>default slot</p>
  </template>
</A>

子组件内

<div class="body">
  <slot></slot>
</div>

不需要名字的插槽

具名插槽

具名插槽其实就是给插槽取个名字。一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时,可以根据这个名字把内容填充到对应插槽中

<div class="header">
  <slot name="header"></slot>
</div>

有名字,父组件就可以通过名字精准插入

<template v-slot:header>
  <p>header slot</p>
</template>

也可以对插槽进行简写,将v-solt:  用#替换即可

作用域插槽

在子组件动态绑定参数 派发给父组件的slot去使用

案例:

A组件

<script setup lang="ts">
import {ref} from "vue";
const dataRef = ref([1,23,4,4,1])
</script>

<template>
  <div class="container">
    <div class="header">
      <slot name="header"></slot>
    </div>
    <div class="body">
      <slot></slot>
    </div>
    <div>
      <slot name="footer" :data="dataRef"></slot>
    </div>
  </div>
</template>

<style scoped>
.container {
  width: 500px;
  height: 500px;
  background-color: #ccc;
  border: 1px solid grey;
  margin: 0 auto;
}
.header {
  height: 50px;
  background-color: #eee;
  border: 1px solid grey;
}
.body {
  height: 400px;
  background-color: #ddd;
  border: 1px solid grey;
}
.footer {
  height: 50px;
  background-color: #eee;
  border: 1px solid grey;
}
</style>

父组件

<script setup lang="ts">
import A from "./components/A.vue";

</script>

<template>
  <A>
    <template v-slot:header>
      <p>header slot</p>
    </template>
    <template v-slot:default>
      <p>default slot</p>
    </template>
    <template #footer="{data}">
      {{data}}
    </template>
  </A>
</template>

<style scoped>

</style>

动态插槽

可以动态的去指定插槽的名字

案例:

<script setup lang="ts">
import A from "./components/A.vue";
import {ref} from "vue";

const slotSelect = ref("header");
const change = () => {
 slotSelect.value = "default";
}
</script>

<template>
  <A>
    <template #[slotSelect]>
      <p>header slot</p>
    </template>
    <template #footer="{data}">
      {{data}}
    </template>
  </A>
  <button @click="change">点我切换</button>
</template>

<style scoped>

</style>

;