likes
comments
collection
share

一文让你彻底理解插槽

作者站长头像
站长
· 阅读数 44

在日常开发中,插槽是非常常用的一个功能,官方文档中定义很多,你可能需要花费不少时间理解,什么默认插槽具名插槽作用域插槽 等等。本文将通过一个 demo 深入的理解插槽的本质,看完之后让给你从根本上理解,并且不会忘记。

1. 插槽的基本使用

我们首先基于vue2实现一个简单demo:

// 父组件 parent.vue 的实现
<template>
  <div class="parent">
    <son>
      <p>我是默认插槽</p>
      <template #slot01>
        <h4>我是具名插槽 slot01</h4>
      </template>
      <template #slot02="{ msg }">
        <p>我是作用域插槽 slot02: {{ msg }}</p>
      </template>
    </son>
  </div>
</template>

<script>
import son from './son';

export default {
  components: {
    son
  }
}
</script>
// 子组件 son.vue 的实现
<template>
  <div class="son">
    <!-- 默认插槽 -->
    <slot></slot>
    <!-- 具名插槽01 -->
    <slot name="slot01"></slot>
    <!-- 作用域插槽 -->
    <slot name="slot02" msg="hello world"></slot>
  </div>
</template>

<script>
export default {

}
</script>

2. 理解插槽

通常情况下我们理解的插槽,就是在子组件son.vue中通过添加 slot标签表示一个插槽的占位,然后在父组件parent.vue 中引入子组件son.vue,然后在<son></son>标签中添加插槽内容。

<!-- 默认插槽 -->
<slot></slot>
<!-- 具名插槽01 -->
<slot name="slot01"></slot>
<!-- 作用域插槽 -->
<slot name="slot02" msg="hello world"></slot>

如上图所示,其实本质是我们在子组件中定义的slot标签会传入一个对象。对象的的属性名称和插槽的名称是关联的,同时属性值是一个函数, 函数的参数关联的是插槽中的传参。如下图所示:

{
    default: funtion() {},
    slot01: function() {},
    slot02: function({msg}) {}
}

实际上上面的对象的属性值返回的函数是一个虚拟节点,虚拟节点占位之后拿到插槽数据然后渲染出来,即我们看到的页面响应结果。我们其实可以动态生成一个子组件,通过在子组件中添加上面的对象的方法来实现一个插槽的效果。

3. 手动实现一个虚拟节点的插槽

// 父组件中引入 new-son.vue
<template>
  <div class="parent">
    <son>
      <p>我是默认插槽</p>
      <template #slot01>
        <h4>我是具名插槽 slot01</h4>
      </template>
      <template #slot02="{ msg }">
        <p>我是作用域插槽 slot02: {{ msg }}</p>
      </template>
    </son>
  </div>
</template>

<script>
import son from './new-son';
export default {
  components: {
    son
  }
}
</script>
// 通过动态生成虚拟节点的方式,生成组件 new-son.vue
<script>
import { h } from 'vue';

export default {
    setup(props, context) {
        console.log('context', context);
        return () => {
            return h("div", null, 'hello world')
        }
    }
}
</script>

// context 日志信息:
{ 
    attrs: Object 
    emit: ƒ () 
    expose: ƒ (exposed) 
    listeners: (...) 
    slots: { 
        default: ƒ () 
        slot01: ƒ () 
        slot02: ƒ () 
    } 
}

如上所示,我们通过render函数生成一个虚拟dom,然后引入到父组件中,浏览器可以看到setup的参数 context的日志信息。日志信息中的slots对象中拿到三个属性,说明我们已经拿到了插槽内容数据,但是还需要渲染出来。

<script>
import { h } from 'vue';

export default {
    setup(props, context) {
        console.log('context', context);
        const { slots } = context || {};
        const _default = slots.default();
        const _slot01 = slots.slot01();
        const _slot02 = slots.slot02({ msg: 'hello world' });
        return () => {
            return h("div", null, [
                ..._default,
                ..._slot01,
                ..._slot02
            ])
        }
    }
}
</script>

4. 总结

插槽本质是通过slot标签确定渲染节点,然后生成特定的对象,该对象属性值生成对应的虚拟dom。

转载自:https://juejin.cn/post/7283152219339096121
评论
请登录