vue中的slot(插槽)

873 阅读4分钟

插槽,也就是slot,slot就是子组件里的一个占位符。这个占位符的内容由父组件控制,父组件往这个子组件模板里面传入什么内容,则这个占位符显示什么内容。

应用领域:插槽在写通用组件这块简直就是神器,尤其是像一些UI组件库,比如ElementUI 组件库,Ant Design UI 组件库。

在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slot 和 slot-scope 这两个目前已被废弃但未被移除且仍在文档中的 attribute。新语法的由来可查阅这份 RFC

插槽有三种类型的: 匿名插槽、具名插槽、作用域插槽。

匿名插槽

匿名插槽的name值,其实是default。 假如我们现在要写通用的模态窗组件:

子组件代码:modal1.vue

<template>
  <div class="slot-modal">
    <!-- 匿名插槽 -->
    <div class="default">
      <div class="title">{{title}}</div>
      <div class="content">
        <slot></slot>
      </div>
      <div class="footer">
        <a-button>{{cancelText}}</a-button>
        <a-button>{{confirmText}}</a-button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    title: {
      type: String,
      default: '标题'
    },
    cancelText: {
      type: String,
      default: '取消'
    },
    confirmText: {
      type: String,
      default: '确定'
    }
  }
}
</script>

上面子组件的slot标签,是一个占位符,内容由父组件控制,父组件在调用子组件的时候,传入什么内容,则子组件显示什么内容。

这样使用的话,这个slot就叫做插槽。这种插槽没有名称,也叫做匿名插槽

父组件代码:

<template>
  <div>
    <modal1 
      title="title" 
      cancelText="cancel" 
      confirmText="ok"
    >
      这是modal内容
    </modal1>
  </div>
</template>

父组件在使用子组件modal1的时候,直接传入了内容'这是modal内容',内容将直接通过子组件的插槽显示出来。效果图:

image.png

具名插槽

有时我们一个组件里面需要多个插槽。我们怎么来区分多个slot,而且不同slot的显示位置也是有差异的.对于这样的情况, 元素有一个特殊的特性:name。这个特性可以用来定义额外的插槽:

  • 注意:一个不带 name 的 出口会带有隐含的名字“default”。就是说,匿名插槽的name值,其实是default

下面依旧写一个模态窗,不过内容区域有两块,一个主内容,一个副内容。

子组件代码:modal2.vue

<template>
  <div class="slot-modal">
    <!-- 具名插槽 -->
    <div class="default">
      <div class="title">
        {{title}}
      </div>
      <div class="content">
        <slot name="content1">主内容</slot>
        <slot name="content2">副内容</slot>
      </div>
      <div class="footer">
        <a-button>
          {{cancelText}}
        </a-button>
        <a-button>
          {{confirmText}}
        </a-button>
      </div>
    </div>
    
  </div>
</template>
<script>
export default {
  props: {
    title: {
      type: String,
      default: '标题'
    },
    cancelText: {
      type: String,
      default: '取消'
    },
    confirmText: {
      type: String,
      default: '确定'
    }
  }
}
</script>

上面定义了两个插槽 ,name分别为‘content1’,'content2',下面开始使用这两个插槽:

在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:

<template>
  <div>
    <modal2  title="title" cancelText="cancel" confirmText="ok">
      <template v-slot:content1>
        <h2>内容1</h2>
      </template>
      <template #content2>
        <p>内容2</p>
      </template>
    </modal2>
  </div>
</template>

注意 v-slot 只能添加在 <template> 上 (只有一种例外情况),这一点和已经废弃的 slot attribute 不同。

具名插槽的缩写:

2.6.0 新增

跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header

作用域插槽

自 2.6.0 起有所更新。已废弃的使用 slot-scope 特性的语法在这里。

有时候,插槽的内容中有需要访问到子组件里面的内容,类似子组件里的slot可以绑定一些当前作用域,从而传出来,使用组件时,插槽的内容就可以拿到slot传出来的数据,父级的插槽内容可用。

如下是子组件myTable.vue,我们想把 item.name 在父组件中替换成 item.englishName

<template>
  <div class="tb">
    <table border="1">
      <tr>
        <th>编号</th>
        <th>名称</th>
        <th>价格</th>
      </tr>
      <tr v-for="item in fruits" :key="item.id">
        <td>{{ item.id }}</td>
        <td>
          <slot name="table" :slotfruits="fruits" test="123">{{ item.name }}</slot>
        </td>
        <td>{{ item.price }}</td>
      </tr>
    </table>
  </div>
</template>

<script>
export default {
  data(){
    return  {

      fruits: [
        {
          id: 1,
          name: '苹果',
          englishName: 'apple',
          price: 5
        },
        {
          id: 2,
          name: '香蕉',
          englishName: 'banana',
          price: 8
        }
      ]
    }
  }

}
</script>

绑定在 元素上的特性被称为插槽 prop,我们在子组件中绑定属性,:slotProps="fruits",test="123"

在父组件中,我们可以访问到这些绑定的属性。 如下父组件:

<my-table>
  <template #table="a">
    {{a.slotProps.englishName}}
  </template>
</my-table>

在父组件中,我们可以通过 v-slot:插槽名=自定义名,来获取子组件插槽传递过来的值。a的值是:

{
    "slotProps": {
        "id": 1,
        "name": "苹果",
        "englishName": "apple",
        "price": 5
    },
    "test": "123"
}

这样,子组件向父组件传递值的方式又多了一种,即作用域插槽的方式。

效果图:

image.png

废弃的语法:

在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slot 和 slot-scope 这两个目前已被废弃但未被移除且仍在文档中的 attribute。新语法的由来可查阅这份 RFC在vue3版本旧语法被移除。

在2.6.0版本之前,语法是这样的写法:

<template slot="default" slot-scope="slotProps">  
  {{ slotProps.msg }}  
</template>

新语法和旧语法写法对比:

<template>
  <a-result>
    <!-- slot新语法 -->
    <template v-slot:title>
      <p>您没有权限哦~</p>
    </template>
    <!-- 新语法 缩写 -->
    <template #subTitle>
      <p>请联系管理员或者联系客服哈</p>
    </template>

    <!-- slot旧语法 -->
    <template slot="icon">
      <a-icon type="meh" />
    </template>
    <template slot="extra">
      <a-button>返回首页</a-button>
    </template>
  </a-result>
</template>