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

Vue插槽有以下3种类型:

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

默认插槽

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

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

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

该组件的使用方式如下:

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

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


具名插槽

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

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

<div class="display-frame">
  <slot name="header">
    <div>Header</div>
  </slot>
  <slot name="main">
    <div>Main</div>
  </slot>
  <slot name="footer">
    <div>Footer</div>
  </slot>
</div>

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

  • slot属性:

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

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

  • v-slot指令:

    v-slot:slot-name
    
    <display-frame>
      <template v-slot:header>
        <h3 slot="header">{{ title }}</h3>
      </template>
      <template v-slot:main>
        <img src="https://blog.linner.asia/images/avatar.jpg">
        <ul>
          <li>姓名:{{ user.name }}</li>
          <li>性别:{{ user.sex }}</li>
          <li>年龄:{{ user.age }}</li>
        </ul>
      </template>
      <template v-slot:footer>
        <a href="https://blog.linner.asia">博客主页</a>
        <a href="https://blog.linner.asia/about/">个人信息</a>
        <a href="https://blog.linner.asia/contact/">联系用户</a>
      </template>
    </display-frame>
    

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

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


渲染作用域

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

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

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

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

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


作用域插槽

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

向父组件传递数据:

<template>
<div class="user-info">
  <slot :username="name" :age="18" :sex="男">
</div>
</template>

<script>
export default {
  name: 'user-info',
  data() {
    name: '张三',
    age: 18,
    sex: '男',
  },
}
</script>

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

  • scope

    <user-info>
      <template scope="userInfo">
        <ul>
          <li>姓名:{{userInfo.username}}</li>
          <li>性别:{{userInfo.sex}}</li>
          <li>年龄:{{userInfo.age}}</li>
        </ul>
      </template>
    </user-info>
    
  • slot-scope

    <user-info>
      <template slot-scope="userInfo">
        <ol>
          <li>姓名:{{userInfo.username}}</li>
          <li>性别:{{userInfo.sex}}</li>
          <li>年龄:{{userInfo.age}}</li>
        </ol>
      </template>
    </user-info>
    
  • v-slot

    <user-info>
      <template v-slot="userInfo">
        <h3>姓名:{{userInfo.username}}</h3>
        <h3>性别:{{userInfo.sex}}</h3>
        <h3>年龄:{{userInfo.age}}</h3>
      </template>
    </user-info>
    

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

    <template>
    <div class="user-info">
      <slot name="main" :username="name" :age="18" :sex="男">
    </div>
    </template>
    
    <user-info>
      <template v-slot:main="userInfo">
        <ul>
          <li>姓名:{{userInfo.username}}</li>
          <li>性别:{{userInfo.sex}}</li>
          <li>年龄:{{userInfo.age}}</li>
        </ul>
      </template>
    </user-info>
    

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

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

    <user-info v-slot="userInfo">
      <ol>
        <li>姓名:{{userInfo.username}}</li>
        <li>性别:{{userInfo.sex}}</li>
        <li>年龄:{{userInfo.age}}</li>
      </ol>
    </user-info>
    

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

<user-info>
  <template slot-scope="{username, sex, age}">
    <h2>姓名:{{username}}</h2>
    <h4>性别:{{sex}}</h4>
    <h4>年龄:{{age}}</h4>
  </template>
</user-info>

<user-info v-slot="{username, sex, age}">
  <h2>姓名:{{username}}</h2>
  <h4>性别:{{sex}}</h4>
  <h4>年龄:{{age}}</h4>
</user-info>

<user-info>
  <template scope="{username, sex, age}">
    <h2>姓名:{{username}}</h2>
    <h4>性别:{{sex}}</h4>
    <h4>年龄:{{age}}</h4>
  </template>
</user-info>

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