• 深入理解Vue插槽的本质
  • 发布于 2个月前
  • 235 热度
    0 评论
在日常开发中,插槽是非常常用的一个功能,官方文档中定义很多,你可能需要花费不少时间理解,什么默认插槽、具名插槽、作用域插槽 等等。本文将通过一个 demo 深入的理解插槽的本质,看完之后让给你从根本上理解,并且不会忘记。
一. 插槽的基本使用
我们首先基于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>
二. 理解插槽
通常情况下我们理解的插槽,就是在子组件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}) {}
}

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


三. 手动实现一个虚拟节点的插槽
// 父组件中引入 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>
四. 总结
插槽本质是通过slot标签确定渲染节点,然后生成特定的对象,该对象属性值生成对应的虚拟dom。


用户评论