一、简单定义、使用 slot

  1. 新建 child 子组件,定义 container 插槽。
<!-- child.vue -->
<template>
  <div>
    <p> child 组件</p>
    <slot name="container"></slot>
  </div>
</template>
  1. 新建 father 组件,引用子组件 childcontainer 插槽。
<!-- father.vue -->
<template>
  <div>
    <p> father 组件</p>
    <child>
     <template slot="container">写法1</template>
      <!-- <template #container>写法2</template> -->
      <!-- <template v-slot:container>写法3</template> -->
    </child>
  </div>
</template>
<script>
import child from "./child.vue";
export default {
  name: "father",
  components: { child }
};
</script>

效果如图所示:
在这里插入图片描述

注:如果 father 组件引用插槽的时候不加具插槽名slot="container", 则默认替换整个 child 组件的内容。

二、slot 变量传值

  1. 在子组件 child 的基础上,给 slot 绑定 message 变量,值为 data 中的 msg 变量值
<!-- child.vue -->
<template>
  <div>
    <p> child 组件</p>
    <slot name="container" :message="msg"></slot>
  </div>
</template>
<script>
export default {
  name: 'child',
  data() {
    return {
      msg: '我是子组件 data 中的 msg 变量'
    }
  }
}
</script>
  1. 在父组件 father 的基础上,接收子组件 childcontainer 插槽传过来的 message 变量。
<!-- father.vue -->
<template>
  <div>
    <p> father 组件</p>
    <child>
      <!-- <template slot="container" slot-scope="{message}">{{message}}</template> -->
      <!-- <template #container="{message}">{{message}}</template> -->
      <template v-slot:container="{message}">{{message}}</template>
    </child>
  </div>
</template>
<script>
import child from "./child.vue";
export default {
  name: "father",
  components: { child }
};
</script>

效果如图所示:
在这里插入图片描述

三、跨组件传递 slot

需求描述:
目前有三个组件,GrandFather.vue 组件、father.vue 组件 和 child.vue 组件, GrandFather.vue 组件 想直接引用并向 child.vue 的 slot 传递变量。
我们期望的正确展示效果如下图所示:
在这里插入图片描述

<!-- 1. child.vue 初始代码状态 -->
<template>
  <div>
    <p> 我是 child 组件</p>
    <slot name="child-container"></slot>
  </div>
</template>
<!-- 2. father.vue 初始代码状态 -->
<template>
  <div>
    <p> 我是 father 组件</p>
    <slot name="father-container"></slot>
    <child>
      <template slot="child-container">这里的值是 father 组件在 child 组件中的 child-container 插槽中展示</template>
    </child>
  </div>
</template>
<script>
import child from "./child.vue";
export default {
  name: "father",
  components: { child }
};
</script>
<!-- 3. GrandFather.vue 初始代码状态 -->
<template>
  <div>
    <p> 我是 GrandFather 组件</p>
    <father>
      <template slot = "father-container">这里的值是 GrandFather 组件 想在 father 组件中的 father-container 插槽中展示</template>
      <!-- 注意:GrandFather 组件想直接引用并替换 child 组件 的 child-container 的值,但按照目前 GrandFather 组件、father组件、child组件,是不可行的。-->
      <template slot = "child-container">这里的值是 GrandFather 组件 想在 child 组件中的 child-container 插槽中展示</template>
    </father>
  </div>
</template>

<script>
import father from "./father.vue";
export default {
  name: "GrandFather",
  components: { father}
};
</script>

在这里插入图片描述
上面的图片结果是初始化代码运行的结果,但似乎与我们期望的代码结果不一致。这是为什么呢?注意在 vue 官网中强调过一点:

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

所以,在 GrandFather 组件中,仅能访问到 father 组件中定义过的插槽,而 child 组件中定义的插槽,GrandFather 组件是无法直接访问到的。那么要怎么实现上面的这种效果,有以下几种方法。

方法1: 多定义一个中间插槽

在不改动 child.vueGrandFather.vue 的前提下,我们单独改动 father.vue 组件如下:

<!-- 2. father.vue -->
<template>
  <div>
    <p> 我是 father 组件</p>
    <slot name="father-container"></slot>
    <child>
      <template slot="child-container"> <!-- 重点1:这里是在 father 组件中 引用 child 组件的 child-container 插槽 -->
        <slot name="child-container"></slot> <!-- 重点2:这里是定义了一个 father 组件的 child-container 插槽  -->
      </template>
    </child>
  </div>
</template>
<script>
import child from "./child.vue";
export default {
  name: "father",
  components: { child }
};
</script>

方法2:使用 scopedSlots 字段 传递作用域插槽

在 vue 官网上有句话叫做:vm.$scopedSlots 在使用 渲染函数 开发一个组件时特别有用。这个方法的改动也需要借助渲染函数。在不改动 child.vueGrandFather.vue 的前提下,我们单独改动 father.vue 组件如下:

<!-- 注释代码:这段 template 代码与下面的 render 函数渲染的代码等价,但我们在 render 函数中借用了 scopedSlots,使得插槽 'child-container' 在当前作用域有效。
<template>
  <div>
    <p> 我是 father 组件</p>
    <slot name="father-container"></slot>
    <child>
      <template slot="child-container">这里的值是 father 组件在 child 组件中的 child-container 插槽中展示</template>
    </child>
  </div>
</template>
-->
<script>
import child from "./child.vue";
export default {
  name: "father",
  components: { child },
  render(createElement) {
    const defaultChildSlotMsg = '这里的值是 father 组件在 child 组件中的 child-container插槽中展示'
    return createElement("div", [
      createElement("p", "我是 father 组件"),
      createElement("slot", this.$slots["father-container"]), // 创建插槽,内容为 GrandFather.vue 中引用 'father-container' 的内容
      createElement("child", {
        scopedSlots: {
          // child.vue 中的插槽名: ()=> GrandFather.vue 引用的插槽名 
          "child-container": () => this.$slots["child-container"] || defaultChildSlotMsg
        }
      }),
    ]);
  }
};
</script>

方法3:动态组件渲染[TODO]

该方法的大概思路为:子组件 主动去获取 父级组件 的 slot 对象,然后再创建一个动态组件进行渲染。将代码恢复至初始状态之后,在不改动GrandFather.vuefather.vue 的前提下,我们单独改动 child.vue 组件。

方法4:Provide/Inject 将Slot主动传递给子节点 [TODO]

附加

  1. $slots 可以拿到当前作用域的所有slot插槽

参考文件:
https://cn.vuejs.org/v2/guide/components-slots.html
https://cn.vuejs.org/v2/guide/render-function.html#%E6%8F%92%E6%A7%BD

Logo

前往低代码交流专区

更多推荐