Vue3 插槽Slot

时间:2024-03-26 20:44:58
 关于作用域

        默认插槽、具名插槽只能访问使用者的作用域,插槽的内容无法访问子组件的数据。Vue模版中表达式只能访问其定义时所处的作用域,这和Javascript的语法作用域规则是一致的。

        如果需要访问子组件中的作用域,则使用作用域插槽

默认插槽

        <slot>元素是一个插槽出口(slot outlet),标示了父元素提供的插槽内容(slot conten)将在哪里被渲染。

        一个小栗子,使用默认插槽展示父组件中的一个值

<template>
  <div class="box">
    <div>子组件中的展示父组件的插槽如下:</div>
    <slot></slot>
  </div>
</template>
<style scoped>
.box {
  display: flex;
  flex-direction: column;
  margin: 15px;
  padding: 10px;
  background-color: antiquewhite;
}
</style>
<template>
  <div class="box">
    <A>
      <div style="padding: 10px;">父组件中使用A子组件的默认插槽,显示父组件中的值:{{ defaultVal }}</div>
    </A>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import A from './component/index.vue'

// 默认插槽
const defaultVal = "A use Val";

</script>
<style scoped>
.box {
  display: flex;
  flex-direction: column;
  margin: 20px;
}
</style>
具名插槽

        有时我们在同一个组件中希望使用多个插槽,此时可以使用具名插槽

        一个小栗子,同一个子组件中同时使用具名插槽和默认插槽,代码如下

<template>
  <div class="box">
    <slot name="top"></slot>
    <slot></slot>
    <slot name="down"></slot>
  </div>
</template>
<style scoped>
.box {
  display: flex;
  flex-direction: column;
  margin: 20px;
  padding: 10px;
  background-color: slategray;
}
</style>
<template>
  <div class="box">
    <B>
      <top>
        <div>父组件中使用子组件B中的具名插槽top,显示父组件中的值:{{ topVal }}</div>
      </top>
      <div>
import { skeletonItemProps } from 'element-plus';
import { sliderButtonProps } from 'element-plus/es/components/slider/src/button';
        <div>父组件中使用子组件B中的默认插槽,显示父组件中的值:{{ bDefault }}</div>
      </div>
      <down>
        <div>父组件中使用子组件B中的具名插槽down,显示父组件中的值:{{ downVal }}</div>
      </down>
    </B>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import B from './component/b.vue'


// 具名插槽与默认插槽一起使用
const topVal = "B use top";
const bDefault = "B default val";
const downVal = "B use down";

</script>
<style scoped>
.box {
  display: flex;
  flex-direction: column;
  margin: 20px;
}
</style>
作用域插槽

        某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点,我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽。

        我们也确实有办法这么做!可以像对组件传递 props 那样,向一个插槽的出口上传递 attributes。

        一个小栗子,在同一子组件中同时使用具名插槽和作用域插槽

<template>
  <div class="box">
    <slot name="top" :aVal="aVal"></slot>
    <slot :bVal="bVal"></slot>
    <slot name="down" :cVal="cVal" :dVal="dVal"></slot>
  </div>
</template>
<script setup>
const aVal = "a val of c component";
const bVal = "b val of c component";
const cVal = "c val of c component";
const dVal = "d val of c component";
</script>
<style scoped>
.box {
  display: flex;
  flex-direction: column;
  margin: 20px;
  padding: 10px;
  background-color: cadetblue;
}
</style>
<template>
  <div class="box">
    <C>
      <template #top="{ aVal }">
        <div>
          {{ introVal }}的具名插槽top,并显示子组件中的值:{{ aVal }}
        </div>
      </template>
      <template #default="{ bVal }">
        <div>
          {{ introVal }}的默认插槽,并显示子组件中的值:{{ bVal }}
        </div>
      </template>
      <template #down="{ cVal, dVal }">
        <div>
          {{ introVal }}的具名插槽down,并显示组件中的值:
            cVal = {{ cVal }}, 
            dVal = {{ dVal }}
        </div>
      </template>
    </C>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import C from './component/c.vue'

// 作用域插槽并同时使用具名插槽
const introVal = "父组件中使用子组件C";

</script>
<style scoped>
.box {
  display: flex;
  flex-direction: column;
  margin: 20px;
}
</style>
无渲染插槽

        一些组件可能只包括了逻辑而不需要自己渲染内容,视图输出通过作用域插槽全权交给了消费者组件。我们将这种类型的组件称为无渲染组件。

        一个小栗子,在子组件中声明x、y两个点,通过按钮增加它们的值,并在使用者的作用域中展示x、y最新的值。

<template>
  <div class="box">
    <div>
      <div style="margin: 30px; padding: 45px; width: 360px; background-color: aqua;" @click="addX">add x</div>
      <div style="margin: 30px; padding: 45px; width: 360px; background-color: aqua;" @click="addY">add y</div>
    </div>
    <slot name="transVal" :x="x" :y="y"></slot>
  </div>
</template>
<script setup>
import { ref } from 'vue'
const x = ref(23); 
const y = ref(25);
const addX = () => {
  let val = x.value;
  val++;
  x.value = val;
}
const addY = () => {
  let val = y.value;
  val++;
  y.value = val;
}
</script>
<style scoped>
.box {
  display: flex;
  flex-direction: column;
  margin: 20px;
  padding: 10px;
  background-color: hotpink;
}
</style>
<template>
  <div class="box">
    <E>
      <template #transVal="{ x, y }">
        E component x = {{ x }}, y = {{ y }}
      </template>
    </E>
  </div>
</template>
<script setup>
import E from './component/e.vue'
</script>
<style scoped>
.box {
  display: flex;
  flex-direction: column;
  margin: 20px;
}
</style>

        在这个例子中子组件中使用了ref,可以看到子组件中的值发生变化时,在父组件中显示的值也有更新