Vue插槽(Slots)用于向组件传递一些模板片段,让子组件在它们的组件中渲染这些片段。插槽也是属于一种父组件到子组件的组件间通行的方式。

Vue插槽有以下3种类型:

  • 默认插槽。
  • 具名插槽。
  • 作用域插槽。

默认插槽

使用默认插槽时,只需在子组件模板中,将要从外部传入的模板内容,使用<slot>进行占位即可。

例如有一个名为display-frame的组件,其模板内容如下:

1<div class="display-frame">
2  <h3>{{title}}</h3>
3  <!-- 默认插槽,等待组件的使用者进行填充 -->
4  <slot>
5    默认插槽内容。使用者没有传递结构时,会显示。
6  </slot>
7</div>

该组件的使用方式如下:

 1<display-frame title="用户信息">
 2  <!-- 
 3    在组件标签体中的内容,
 4    会被插入到组件中使用了 <slot> 的位置
 5   -->
 6  <ul>
 7    <li>姓名:{{ user.name }}</li>
 8    <li>性别:{{ user.sex }}</li>
 9    <li>年龄:{{ user.age }}</li>
10  </ul>
11</display-frame>

注:插槽在使用过程中,<slot>标签并不限制使用次数,意味着可以在模板中显示两个相同的插槽内容。并且,在使用标签内容插入模板时,标签内容并不需要像组件模板一样需要在外部有一个作为根的元素。


具名插槽

具名插槽就是指给组件插槽位置指定一个名称。外部在使用组件标签时,可以通过插槽名称指定模板要插入到组件中的位置。

定义插槽名称使用的是<slot>标签的name属性:

 1<div class="display-frame">
 2  <slot name="header">
 3    <div>Header</div>
 4  </slot>
 5  <slot name="main">
 6    <div>Main</div>
 7  </slot>
 8  <slot name="footer">
 9    <div>Footer</div>
10  </slot>
11</div>

上方定义了3个插槽,分别是headermainfooter。使用方式有两种:

  • slot属性:

    1slot="slot-name"
    
     1<display-frame>
     2  <h3 slot="header">{{ title }}</h3>
     3  <img slot="main" src="https://blog.linner.asia/images/avatar.jpg">
     4  <ul slot="main">
     5    <li>姓名:{{ user.name }}</li>
     6    <li>性别:{{ user.sex }}</li>
     7    <li>年龄:{{ user.age }}</li>
     8  </ul>
     9  <template slot="footer">
    10    <a href="https://blog.linner.asia">博客主页</a>
    11    <a href="https://blog.linner.asia/about/">个人信息</a>
    12    <a href="https://blog.linner.asia/contact/">联系用户</a>
    13  </template>
    14</display-frame>
    

    slot属性可以作用在普通元素标签上,也可以作用在<template>上。并且同个插槽可以同时存放多个元素(模板),只需要定义多个具有相同的slot属性的元素,或者使用<template>将它们包裹起来即可。

  • v-slot指令:

    1v-slot:slot-name
    
     1<display-frame>
     2  <template v-slot:header>
     3    <h3 slot="header">{{ title }}</h3>
     4  </template>
     5  <template v-slot:main>
     6    <img src="https://blog.linner.asia/images/avatar.jpg">
     7    <ul>
     8      <li>姓名:{{ user.name }}</li>
     9      <li>性别:{{ user.sex }}</li>
    10      <li>年龄:{{ user.age }}</li>
    11    </ul>
    12  </template>
    13  <template v-slot:footer>
    14    <a href="https://blog.linner.asia">博客主页</a>
    15    <a href="https://blog.linner.asia/about/">个人信息</a>
    16    <a href="https://blog.linner.asia/contact/">联系用户</a>
    17  </template>
    18</display-frame>
    

    v-slot指令在使用时,只能在<template>上使用,不能在其它元素标签上使用。

默认插槽就是没有提供name<slot>。没有提供name<slot>出口会隐式地命名为default


渲染作用域

插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的。例如:

1<span>{{ message }}</span>
2<display-frame>{{ message }}</display-frame>

两个标签中的{{ message }}插值表达式渲染的内容都是一样的。

需要注意的是,插槽的内容无法访问子组件的数据(无法直接访问)。因为Vue模板中的表达式只能访问其定义时所处的作用域,这和JavaScript的词法作用域规则是一致的。

也就是说,在哪个文件中定义的模板,其数据的作用域就在哪个文件,其表达式访问的也是该文件中的数据。


作用域插槽

作用域插槽可以让组件在定义插槽位置时,将组件中的一些数据,通过插槽<slot>,像props传递数据那样,将数据传递给父组件使用。

向父组件传递数据:

 1<template>
 2<div class="user-info">
 3  <slot :username="name" :age="18" :sex="男">
 4</div>
 5</template>
 6
 7<script>
 8export default {
 9  name: 'user-info',
10  data() {
11    name: '张三',
12    age: 18,
13    sex: '男',
14  },
15}
16</script>

父组件接收数据,并为子组件定义插槽模板,有3种方式:

  • scope

    1<user-info>
    2  <template scope="userInfo">
    3    <ul>
    4      <li>姓名:{{userInfo.username}}</li>
    5      <li>性别:{{userInfo.sex}}</li>
    6      <li>年龄:{{userInfo.age}}</li>
    7    </ul>
    8  </template>
    9</user-info>
    
  • slot-scope

    1<user-info>
    2  <template slot-scope="userInfo">
    3    <ol>
    4      <li>姓名:{{userInfo.username}}</li>
    5      <li>性别:{{userInfo.sex}}</li>
    6      <li>年龄:{{userInfo.age}}</li>
    7    </ol>
    8  </template>
    9</user-info>
    
  • v-slot

    1<user-info>
    2  <template v-slot="userInfo">
    3    <h3>姓名:{{userInfo.username}}</h3>
    4    <h3>性别:{{userInfo.sex}}</h3>
    5    <h3>年龄:{{userInfo.age}}</h3>
    6  </template>
    7</user-info>
    

    与前两种不同的是,v-slot可以指定插槽位置:

    1<template>
    2<div class="user-info">
    3  <slot name="main" :username="name" :age="18" :sex="男">
    4</div>
    5</template>
    
    1<user-info>
    2  <template v-slot:main="userInfo">
    3    <ul>
    4      <li>姓名:{{userInfo.username}}</li>
    5      <li>性别:{{userInfo.sex}}</li>
    6      <li>年龄:{{userInfo.age}}</li>
    7    </ul>
    8  </template>
    9</user-info>
    

    scopeslot-scope指定卡槽需要配合slot属性。

    v-slot可以直接在组件标签上使用:

    1<user-info v-slot="userInfo">
    2  <ol>
    3    <li>姓名:{{userInfo.username}}</li>
    4    <li>性别:{{userInfo.sex}}</li>
    5    <li>年龄:{{userInfo.age}}</li>
    6  </ol>
    7</user-info>
    

这3种使用方式都支持解构赋值:

 1<user-info>
 2  <template slot-scope="{username, sex, age}">
 3    <h2>姓名:{{username}}</h2>
 4    <h4>性别:{{sex}}</h4>
 5    <h4>年龄:{{age}}</h4>
 6  </template>
 7</user-info>
 8
 9<user-info v-slot="{username, sex, age}">
10  <h2>姓名:{{username}}</h2>
11  <h4>性别:{{sex}}</h4>
12  <h4>年龄:{{age}}</h4>
13</user-info>
14
15<user-info>
16  <template scope="{username, sex, age}">
17    <h2>姓名:{{username}}</h2>
18    <h4>性别:{{sex}}</h4>
19    <h4>年龄:{{age}}</h4>
20  </template>
21</user-info>

适用于:数据在组件中定义,但根据数据生成的结构需要组件的使用者来决定。