Vue Slot

如何重新解读 vue slot?

Posted by My on April 24, 2022

官网文档,对插槽做出相关的定义,例如 「默认插槽」、「具名插槽」和「作用域插槽」 。那现在我们用另一种方式来解析 插槽(slot) 的本质。

我们姑且将定义声明插槽的组件叫做 「子组件(comp)」。 现在子组件有这样的格式:

1
2
3
4
5
6
7
8
9
//comp.vue

<template>
  <div>
    <slot></slot>
    <slot name="slot1"></slot>
    <slot name="slot2" msg="hello"></slot>
  </div>
</template>

在父组件使用的时候,按照对应的标识,传入标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
//index.vue

<Comp>
      <p>默认插槽</p>

      <template #slot1>
        <p>具名插槽</p>
      </template>

      <template #slot2="{ msg }">
        <p>作用域插槽:</p>
      </template>
    </Comp>

现在,我们可以用 「函数调用」 的思想去理解插槽。

在使用 comp 组件时相当于 「传递了一个对象」,该对象有个三个属性,「属性值均为一个函数」。

1
2
3
4
5
{
    default: function(){},
    slot1: function(){},
    slot2: function(){}
  }

而在组件 comp 中则是通过 「调用接收的对象的对应方法」。即 <slot></slot> 为调用 props.default()

(PS: 说着说着感觉就有点 「react」 的味道了)

现在我们使用用 js 来实现这个 comp 组件。

1
2
3
4
5
6
7
8
9
10
//comp.js

import { createElementVNode } from "vue";
export default {
  setup() {
    return () => {
      return createElementVNode("div", null, []); // 创建虚拟节点
    };
  },
};

拿到参数,

1
2
3
4
5
6
7
8
9
export default {
  setup(props, ctx) {
    //两个参数 第二个参数包含了一些其他信息
    console.log("ctx", ctx);
    return () => {
      return createElementVNode("div", null, []); // 创建虚拟节点
    };
  },
};

执行对应的方法,

1
2
3
4
5
6
7
8
export default {
  setup(props, { slots }) {
    const defaultVNode = slots.default(); // 调用默认方法
    return () => {
      return createElementVNode("div", null, [...defaultVNode]);
    };
  },
};

因此,原先的 comp.vue 可以写成 comp.js,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//comp.js

import { createElementVNode } from "vue";
export default {
  setup(props, { slots }) {
    const defaultVnode = slots.default();
    const slot1Vnode = slots.slot1();
    const slot2Vnode = slots.slot2({
      msg: "hello",
    });
    return () => {
      return createElementVNode("div", null, [
        ...defaultVnode,
        ...slot1Vnode,
        ...slot2Vnode,
      ]);
    };
  },
};