Vue的 slot 到底是个啥?

784 阅读3分钟

Vue的 slot 到底是个啥?老是记不住使用方法。今天就好好整理下这块知识。

Vue2 中 Slot 的实现原理与使用方法

一、Slot 的基本概念‌

Slot(插槽)是 Vue 组件化开发中用于内容分发的重要机制,允许父组件向子组件传递模板片段或 DOM 结构。Vue2 的插槽分为三种类型:

  • 默认插槽‌:父组件传递的内容默认填充到子组件的 标签位置。
  • 具名插槽‌:通过 name 属性标识插槽位置,实现精准分发。
  • 作用域插槽‌:子组件向父组件传递数据,父组件基于子组件的数据渲染插槽内容。

二、Slot 的实现原理‌

Vue2 的插槽实现分为编译阶段和渲染阶段:

1. 编译阶段‌

  • 父组件模板编译‌: 插槽内容会被编译为一个函数(_t 函数,即 renderSlot),保存在子组件的 $scopedSlots$slots中。作用域插槽的内容被包装成的函数,接收子组件传递的数据作为参数。
  • 子组件模板编译‌: <slot> 标签会被替换为 _t 函数调用,例如 <slot name="header"> 会编译为 _t("header")。

2. 渲染阶段‌

普通插槽‌(默认插槽、具名插槽):

  • 父组件的内容在父组件的作用域‌提前渲染‌,结果作为子组件的 $slots。
  • 子组件通过 this.$slots[name] 获取对应的 VNode 并渲染。

作用域插槽‌:

  • 父组件的内容延迟到子组件作用域中渲染,子组件通过 this.$scopedSlots[name](props) 传递数据。
  • 父组件接收数据并生成最终的 VNode。

3. 关键源码逻辑‌

  • vm.$slots: 存储静态插槽内容(普通插槽)。
  • vm.$scopedSlots: 存储作用域插槽函数。
  • renderSlot(): 负责执行插槽函数并返回 VNode。

三、Slot 的使用方法‌

  1. 默认插槽‌

子组件‌:通过 <slot> 定义默认位置。

<!-- Child.vue -->
<template>
  <div>
    <slot>默认内容(可选)</slot>
  </div>
</template>

父组件‌:直接传递内容到子组件标签内部。

<Child>
  <p>这是父组件传递的内容</p>
</Child>
  1. 具名插槽‌

子组件‌:通过 指定插槽名称。

<!-- Child.vue -->
<template>
  <div>
    <slot name="header"></slot>
    <slot></slot>
    <slot name="footer"></slot>
  </div>
</template>

父组件‌:通过 v-slot:name 或 #name 指定内容分发位置。

<Child>
  <template v-slot:header>
    <h1>这是头部</h1>
  </template>
  <p>这是默认内容</p>
  <template #footer>
    <p>这是底部</p>
  </template>
</Child>
  1. 作用域插槽‌

子组件‌:在 <slot> 上绑定数据(类似 props)。

<!-- Child.vue -->
<template>
  <div>
    <slot :user="user">{{ user.name }}</slot>
  </div>
</template>
<script>
export default {
  data() {
    return { user: { name: "Alice", age: 25 } };
  },
};
</script>

父组件‌:通过 v-slot:default="props" 接收子组件数据。

<Child>
  <template v-slot:default="slotProps">
    <p>姓名:{{ slotProps.user.name }}</p>
    <p>年龄:{{ slotProps.user.age }}</p>
  </template>
</Child>

四、注意事项‌

  • 作用域隔离‌:父组件模板中的插槽内容默认访问父组件数据,作用域插槽通过子组件传递数据。
  • 语法兼容‌:Vue 2.6+ 推荐使用 v-slot 指令(缩写为 #),旧版 slot="name" 和 slot-scope 语法仍兼容。
  • 性能优化‌:作用域插槽的函数特性可能导致子组件频繁更新,可通过缓存优化。

五、总结‌

  • 原理核心‌:插槽本质是父组件内容编译为函数,子组件在渲染时调用函数并传入数据。
  • 使用场景‌:
    • 默认插槽‌:简单内容分发。
    • 具名插槽‌:多区块内容分发。
    • 作用域插槽‌:子组件数据驱动父组件内容渲染。

通过合理使用插槽,可以极大提升组件的灵活性和复用性。