你使用的Vue插槽(slot)语法可能已经过时了

您好,我是沧沧凉凉,是一名前端开发者,目前在掘金知乎以及个人博客上同步发表一些学习前端时遇到的趣事和知识,欢迎关注。


插槽在Vue中是一个十分常见以及经常会使用到的功能,它的存在大大提高了组件的复用性,很多时候我们都需要使用插槽来将组件进行复用。

在平时的插槽使用中,我们大部分时间都仅仅使用了插槽的一些简单功能,但其实插槽还有更多高级的功能,比如说能将子组件中的数据传递到父组件中,父组件中能够访问到该传递的数据。

因为一些原因,官方已经废弃了之前插槽的用法,相信只要使用Vue框架的人肯定都用过Element UI,但其实Element UI的表格插槽使用方式其实现在已经过时了,就如下面代码。

<template slot-scope="scope">
    <el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
    <el-button type="text" size="small">编辑</el-button>
</template>

官方声明在Vue3.x版本时,已经不再对这种插槽使用方式进行支持。

1. 写法

插槽的使用是非常简单的,这里要分两种情况:

  • 默认插槽:即<slot />这种不带name属性的为默认插槽,但是在某些情况下,我们需要在不同的地方插入不同的值,所以就需要使用到具名插槽。
  • 具名插槽:即<slot name="header" />这种带有name属性的插槽。

其实<slot />这种默认插槽是附带了name属性的,只是它的name属性为default,在一般情况下我们会将它进行省略。

下面我们分别来看一下默认插槽和具名插槽。

<template>
  <div>
    <header>
      <!-- 这里是具名插槽 -->
      <slot name="header" />
    </header>
    <!-- 这是默认插槽 -->
    <!-- 下面这两种插槽本质都是默认插槽 -->
    <slot />
    <slot name="default" />
  </div>
</template>

<script>
export default {
  name: "SlotTest"
};
</script>

<style>
header {
  font-size: 30px;
  font-weight: bold;
}
</style>

上面这段代码分别声明了默认插槽和具名插槽,其中同名的插槽是可以多次声明的,并不是只能声明一次。

当声明多次的时候,插入的内容也会变成两份。例如我们使用以下默认插槽:

<slot-test>
  Hello default slot!
</slot-test>

这个时候运行项目,会发现在浏览器中会出现两个Hello default slot!

那么大概知道了这些内容后,我们就来看一下具名插槽和默认插槽分别是怎么用的。

推荐写法

官方在2.6.0版本中,修改了插槽的用法,现在推荐使用<template v-slot:header>这种写法来调用具名插槽。

<slot-test>
  <!-- 具名插槽的用法 -->
  <template v-slot:header>
    Hello header slot!
  </template>
  <!-- 这是一种默认插槽的用法 -->
  <template v-slot:default>
    Hello header slot!
  </template>
  <!-- 这是另一种默认插槽的用法 -->
  Hello header slot!
</slot-test>

需要注意的是,v-slot属性只有当标签为<template />才会生效,如果是其它标签是无法调用插槽的,例如:

<div v-slot:header>
    Hello header slot!
</div>

不仅不能调用,甚至还会报错。

值得注意的是,v-slotv-onv-bind一样,也有缩写,那就是#,所以上面的代码也可以写成下面这样:

<slot-test>
  <!-- 具名插槽的用法 -->
  <template #header>
    Hello header slot!
  </template>
  <!-- 这是一种默认插槽的用法 -->
  <template #default>
    Hello header slot!
  </template>
  <!-- 这是另一种默认插槽的用法 -->
  Hello header slot!
</slot-test>

过时写法

由于官方文档的排版问题,我在最开始读文档的时候搞不清哪个是过时用法,反复读了几遍后,终于发现<div slot="header"></div>这种语法使用插槽就为过时语法,官方在文档说在Vue3中不会再兼容这种写法:

<slot-test>
  <div slot="header">
    Hello header slot!
  </div>
</slot-test>

与上面的推荐写法不同,slot不仅仅可以声明在<template />标签上,还可以声明在其它的HTML标签上面,比如上面的div标签。

2. 向父组件传值

有时候我们需要将子组件中的属性值传递到父组件,让父组件在使用插槽的时候能够取得这些值,例如下面的代码:

<template>
  <div>
    <header>
      <!-- 这里是具名插槽 -->
      <slot name="header" />
    </header>
    <!-- 这是默认插槽 -->
    <!-- 下面这两个插槽本质是同一个 -->
    <slot />
    <slot name="default" />
  </div>
</template>

<script>
export default {
  name: "SlotTest",
  data() {
    return {
      // 该值希望在父组件中可以获取并且调用
      describe: "插槽的使用"
    };
  }
};
</script>

<style>
header {
  font-size: 30px;
  font-weight: bold;
}
</style>

可以看到,我们希望在父组件中获取到子组件的describe值,那么这种情况怎么进行处理呢?

<slot-test>
  {{ describe }}
  Hello header slot!
</slot-test>

像上面这种方式直接尝试调用describe属性显然是不现实的,会直接进行报错,那么我们就来看一下究竟如何在父组件中获得describe属性值吧。

2.1 推荐写法

官方在2.6.0版本中同样修改了这一部分的写法。

首先我们要想在父组件中获得该属性,就必须在插槽上面进行传递:

<div>
  <header>
    <slot name="header" :describe="describe" />
  </header>
  <slot :describe="describe" />
</div>

这个时候在父组件中就可以获取到传递出来的describe值:

<slot-test v-slot="slotProps">
  {{ slotProps.describe }}
</slot-test>

注意:子组件中传出来的是一个对象,即:{ "describe": "插槽的使用" }。并不是describe的值,所以slotProps这个变量其实取什么名字都是可以的。

具名插槽也是同样:

<slot-test>
  <template #header="slotProps">
    {{ slotProps.describe }}
  </template>
  <!-- 一旦有具名插槽,默认插槽就必须这样写 -->
  <template v-slot:default="slotProps">
    {{ slotProps.describe }}
  </template>
</slot-test>

需要值得注意的是,一旦有了具名插槽,默认插槽就不能再像上面一样,直接声明到slot-test上,这会导致作用域不明确而报错。

2.1.1 解构插槽Prop

插槽Prop是可以直接通过ES6语法来解构的:

<slot-test>
  <template #header="{ describe }">
    {{ describe }}
  </template>
  <!-- 一旦有具名插槽,默认插槽就必须这样写 -->
  <template v-slot:default="{ describe }">
    {{ describe }}
  </template>
</slot-test>

2.2 过时写法

过时写法看一下就好了,虽然Element UI的表格那块插槽示例依然还是这种写法,但是Vue官方已经不推荐了。

<slot-test>
  <template slot="header" slot-scope="slotProps">
    {{ slotProps.describe }}
  </template>
  <!-- 这是过时写法 -->
  <template slot-scope="slotProps">
    {{ slotProps.describe }}
  </template>
</slot-test>

3. 最后

插槽的用法是非常灵活的,知道了上面的那些基础知识后,你也可以制作出一些十分方便的组件。

同时非常推荐去读一下官方文档中的插槽介绍,你可能会发现一些新大陆。