Vue3插槽的使用

5,826 阅读4分钟

Vue3插槽的使用

如果想要子组件渲染父组件指定的数据,可以使用props,将父组件的数据传入进去。但如果我们要子组件渲染一段父组件指定的内容呢?

Vue提供了一个特别的组件 < slot>(插槽),它的作用就是在子组件中标记一个位置,然后将父组件中传入的内容渲染到它所以的位置中。

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

1. 基本使用

子组件:通过 < slot>< /slot>标签来定义插槽

//hello-world组件中
<div>
    <!--定义插槽-->
    <slot></slot>
</div>

父组件:

<hello-world>
      <!--向插槽插入内容-->
      <button>按钮</button>
</hello-world>

在子组件中通过 <slot>定义插槽,父组件中插入的内容将被放到<slot>定义的地方进行渲染。

最终子组件的渲染结果:

<div>
    <button>按钮</button>
</div>

2. 插槽的作用域

**插槽的内容可以访问到父组件的数据,但是不能访问到子组件的数据。**这是因为插槽的内容是在父组件总进行编译,然后将编译好的内容传给子组件进行渲染。子组件并不会解析和编译,只是负责渲染父组件编译好的插槽内容。

插槽的内容是由它的定义者进行解析编译的,然后将编译好的内容传入到插槽中,由插槽进行渲染。

interface Person {
   name: string,
   age: number
}
const person = reactive<Person>({
   age: 18,
   name: "tom"
})
<hello-world>
   <!--向插槽插入内容-->
   <button>{{person.name}}</button> //可以使用person。
</hello-world>

引用Vue官网文档的一段话:

插槽内容无法访问子组件的数据,请牢记一条规则:

任何父组件模板中的东西都只被编译到父组件的作用域中;而任何子组件模板中的东西都只被编译到子组件的作用域中。

3.插槽的默认内容

可以为插槽定义默认内容,就和给props定义默认值一样,如果在父组件中没有传入,则使用默认内容。反之如果父组件有传入内容,则使用父组件传入的内容。

子组件中:

<div>
   <slot>
      <!--默认内容-->
      <button>按钮1</button>
   </slot>
</div>

父组件中没有传入值

<hello-world></hello-world>

渲染结果:渲染默认内容

<div>
   <button>按钮1</button>
</div>

父组件传入值

<hello-world>
   <button>按钮2</button>
</hello-world>

渲染结果:渲染父组件传入的内容

<div>
   <button>按钮2</button>
</div>

4. 具名插槽

具名插槽:就是给插槽定义一个标识,因为有时候一个组件中可能会定义多个插槽,为了能够区分他们,就需要给他们定义一个标识。

子组件:通过name属性给插槽定义标识:

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

父组件:通过 < template v-slot:标识>来将内容插入到指定的插槽中。

<hello-world>
   <template v-slot:header>
      <button>按钮01</button>
   </template>
   <template v-slot:main>
      <button>按钮02</button>
   </template>
</hello-world>

注意:如果一个插槽没有指定name属性,则会使用它的默认值:default

子组件:

<div>
   <slot></slot>
</div>

父组件使用:

<hello-world>
   <template v-slot:default>
      <button>按钮</button>
   </template>
</hello-world>

上面的写法等价于:

<hello-world>
   <button>按钮2</button>
</hello-world>

直接使用就是传入给默认插槽

v-slot 有对应的简写方式 #,因此 <template v-slot:header> 可以简写为 <template #header>

<hello-world>
   <template v-slot:header>
      <button>按钮01</button>
   </template>
</hello-world>

等价于:

<hello-world>
   <template #header>
      <button>按钮01</button>
   </template>
</hello-world>

5.作用域插槽

在上面我们有提到过,插槽的内容是无法获取到子组件中的数据的。如果我们想要获取子组件中的数据呢?

*可以在给插槽组件定义属性,在插槽上定义的属性,会因为属性的透传的特性,流入到组件内的标签元素中。*当然,如果只是这样我们还是没有办法去获取和使用的。

我们需要使用Vue给我们提供了一个指令 v-slot,这个指令不止能用来指定具名插槽,还可以用来获取 < slot>组件的透传对象。

slot组件上定义透传属性:

interface Person {
   name: string,
   age: number
}

const person = reactive<Person>({
   age: 18,
   name: "tom"
})
<div>
   <slot :brand="phone.brand" class="item" :price="phone.price"></slot>
</div>

通过 v-slot指令获取透传对象

<hello-world v-slot="row">
   <div>{{ row.class }}</div>
   <div>{{ row.price }}</div>
   <div>{{ row.brand }}</div>
</hello-world>

​ 上面的例子是获取默认插槽的透传对象。如果是具名插槽该怎么获取?还是使用 v-slot指令来获取,不过使用方式有点变化。

定义具名插槽:

<div>
   <slot name="slot1" class="slot1" title="标题1"></slot>
   <slot name="slot2" class="slot2" title="标题2"></slot>
</div>

获取具名插槽的透传对象

<hello-world>
   <template v-slot:slot1="row1">
      <div>{{ row1.title }}</div> //标题1
   </template>
   <template v-slot:slot2="row2">
      <div>{{ row2.title }}</div> //标题2
   </template>
</hello-world>

通过 v-slot:插槽标识="透传对象"的方法来获取具名插槽的透传对象