# 1、使用思考

  经过《自定义事件》和《【全局事件总线】》这两个章节的洗礼,现在是不是满脑子都是:父子组件、数据传递之类的东西 😂。

  但是,但是,总是传递一点点的数据怎么可以满足,格局小了。要传就传大的:template 模板怎么样?

  在前面,我们对子组件的使用,仅仅只是在属性层面,换一个角度,在内容层面会发生什么情况呢?

  • 父组件
<template>
  <hello-world>随便写一下内容吧</hello-world>
</template>
  • 子组件
<template>
  <div class="hello">
    <!-- 插槽:接收内容 -->
    <slot></slot>
  </div>
</template>

# 2、默认插槽

  但有时候,我们在编写子组件的时候,父组件使用插槽时可能并不会如我们所愿:他不会向插槽提供内容。

  此时我们就是设置一个默认内容以备不时之需 —— 默认插槽。

<template>
  <div class="hello">
    <!-- 为插槽设置默认内容 -->
    <slot>这是子组件默认内容</slot>
  </div>
</template>

# 3、具名/动态插槽

  当然,像一些组件库中若使用插槽的话,大概率会提供多个插槽供我们使用,这个时候我们就需要:

父组件中使用 v-slottemplate 标签 ✍ 上,可以简写为 #
子组件中使用 name 属性(slot 标签上)

应用:vant 导航栏

  以其导航组件中的 NavBar 导航栏为例:通过#right 可以自定义 vant 组件库封装好的导航右边的内容。

<van-nav-bar title="标题" left-text="返回" left-arrow>
  <template #right>
    <van-icon name="search" size="18" />
  </template>
</van-nav-bar>

  这样就可以将内容和插槽区域一一对应了(如下面的 title):

  • 父组件
<!-- 1、具名插槽 -->
<template>
  <div class="home">
    <hello-world>
      <!--  -->
      <template #title>
        <h3>量身定制</h3>
      </template>
    </hello-world>
  </div>
</template>

<!-- =========================== -->

<!-- 2、动态插槽 -->
<template>
  <div class="home">
    <hello-world>
      <!--  -->
      <template v-slot:[name]>
        <h3>量身定制</h3>
      </template>
    </hello-world>
  </div>
</template>
  • 子组件
<template>
  <div class="hello">
    <!--  -->
    <slot name="title"></slot>
  </div>
</template>

# 4、作用域插槽 🎈

  前面我们传的都是纯 template 模板,那在父组件中:如果子组件标签内 template 模板涉及到要使用子组件中的数据怎么办?

提示

  作用域插槽,在一些开源项目、组件封装中应用比较广泛,比如 element-plus 中的插槽(其提供的 scope):

应用:element 表格
  • 自定义表头/列模板
<template>
  <el-table :data="tableList">
    <el-table-column>
      <template #header>
        <!-- …… -->
      </template>
      <template #default="scope">
        <!-- scope.$index, scope.row -->
      </template>
    </el-table-column>
  </el-table>
</template>

  当然,你可以单独的使用自定义事件来获取子组件数据,但这违背了子组件使用插槽的初衷,具体解决方案为:

在父组件中,我们使用 slot 属性去匹配子组件插槽的 name 属性,然后我们可以通过 slot-scope 属性获取子组件插槽上通过 props 传递的数据(不同于常规的父传子的 props 选项 接收数据)。

  上述方案也就是 vue 中的作用域插槽,其中还有一些细节:

  • slot 标签上使用的 props(绑定在 <slot> 元素上的 attribute 被称为插槽 prop
  • slot 属性 和 slot-scope 属性在 vue2.6.0 开始合并为一个新指令 v-slot
关于 v-slot 指令 🤔 的发展史

  默认插槽中,它们一一对应的值为default

  -v-slot可以简写为 #,例如:v-slot:default 和 #default

<template #default>
  <h3>量身定制</h3>
</template>

<!-- …… -->

<slot name="default"></slot>

  -v-slot2.6.0 版本开始为具名插槽和作用域插槽引入新指令(语法糖)

<template>
  <div>
    <hello-world>
      <!-- 1、新命令 -->
      <template #title="slotProps">
        <!-- 略 -->
      </template>

      <!-- 2、旧命令 -->
      <template slot="title" slot-scope="slotProps">
        <!-- 略 -->
      </template>
    </hello-world>
  </div>
</template>

<!-- …… -->

<slot name="title" v-bind="propsObj"></slot>
简单使用
  • 子组件
<template>
  <div class="hello">
    <!-- 定义一个插槽prop传送数据 -->
    <slot name="title" :classifyList="slotData" :prop="classify">
      <!-- 默认内容 -->
      <div>
        <h3>title</h3>
        <ul>
          <li>one</li>
          <li>two</li>
          <li>three</li>
        </ul>
      </div>
    </slot>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      slotData: [
        {
          title: '水果',
          lis: ['苹果', '香蕉', '橙子']
        },
        {
          title: '动物',
          lis: ['熊猫', '老鹰', '猴子']
        }
      ]
    }
  }
}
</script>
  • 父组件
<template>
  <div class="home">
    <h3>HomeView组件内容</h3>

    子组件内容如下:
    <hello-world>
      <!-- 数据、位置都🚩可以确定 -->
      <template #title="slotProps">
        <!-- {{ slotProps.classifyList, slotProps.props }} -->
        <div v-for="(item, index) in slotProps.classifyList" :key="index">
          <h3>{{ item.title }}</h3>
          <ul v-for="(i, idx) in item.lis" :key="idx">
            <li>{{ i }}</li>
          </ul>
        </div>
      </template>
    </hello-world>
  </div>
</template>

  进一步的,我们还可以通过解构赋值简化父组件获取的 slotProps 数据操作:

<template>
  <div class="home">
    <hello-world>
      <template #title="{ scope }">
        <!-- 略 -->
      </template>
    </hello-world>
  </div>
</template>
拓展: 独占 👀 默认插槽

使用:

<template>
  <div class="home">
    <hello-world>
      <!-- <template v-slot:default="slotProps"> -->
      <!-- 简写 -->
      <template v-slot="slotProps">
        <!-- 略 -->
      </template>
    </hello-world>
  </div>
</template>

应用:

  如果只有一个默认插槽,那么 template 可以省略(直接写在子组件标签上)

<template>
  <div class="home">
    <hello-world v-slot="slotProps">
      <!-- 略 -->
    </hello-world>
  </div>
</template>

# 5、拓展

  如果你使用的 vue 版本低于 2.6.0,你就得使用旧命令了:

<hello-world>
  <!-- 1、新命令 -->
  <template #title="slotProps">
    <!-- 略 -->
  </template>

  <!-- 2、旧命令 -->
  <template slot="title" slot-scope="slotProps">
    <!-- 略 -->
  </template>
</hello-world>
更新于 : 8/7/2024, 2:16:31 PM