vue2函数式组件 functional中关于 slots 取值、透传子组件方法

在Vue 2中,函数式组件(functional component) 本身没有实例,只能通过 render 函数接收 context 参数来处理插槽。要实现动态插槽,需要结合 context.slots() 方法和动态插槽名称来灵活分发内容。以下是详细实现方案:

一、核心概念回顾

  1. 函数式组件特点

    • 无状态(无 data、computed)、无实例(无 this)
    • 依赖 context 参数获取 props、children、slots 等
    • 性能更高,适合纯展示型组件
  2. 动态插槽需求

    • 插槽名称不固定(如根据 props 或外部数据动态生成)
    • 需灵活控制插槽内容的分发逻辑

二、基础实现:动态插槽分发

通过 context.slots() 方法可以获取父组件传递的插槽,结合动态名称实现分发。

1. 子组件(函数式组件)

arduino 复制代码
// DynamicFunctionalSlot.js
export default {
  functional: true,
  props: {
    // 动态插槽名称(由父组件传递)
    slotName: {
      type: String,
      required: true
    }
  },
  render(h, context) {
    const { props, slots } = context;
    // 获取父组件传递的指定插槽内容
    const dynamicSlotContent = slots()[props.slotName];
    
    return h('div', [
      // 静态插槽
      h('div', { class: 'header' }, slots().header),
      // 动态插槽
      h('div', { class: 'dynamic-content' }, dynamicSlotContent),
      // 默认插槽
      h('div', { class: 'footer' }, slots().default)
    ]);
  }
};

2. 父组件使用

xml 复制代码
<template>
  <div>
    <DynamicFunctionalSlot :slot-name="currentSlot">
      <!-- 静态插槽:header -->
      <template slot="header">
        <h1>固定头部</h1>
      </template>
      
      <!-- 动态插槽候选:slotA、slotB -->
      <template slot="slotA">
        <p>动态内容 A</p>
      </template>
      <template slot="slotB">
        <p>动态内容 B</p>
      </template>
      
      <!-- 默认插槽 -->
      <p>默认底部内容</p>
    </DynamicFunctionalSlot>
    
    <!-- 切换动态插槽 -->
    <button @click="currentSlot = 'slotA'">显示插槽A</button>
    <button @click="currentSlot = 'slotB'">显示插槽B</button>
  </div>
</template>

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

export default {
  components: { DynamicFunctionalSlot },
  data() {
    return {
      currentSlot: 'slotA' // 默认显示slotA
    };
  }
};
</script>

三、高级场景:动态生成插槽

如果需要动态生成多个插槽(如根据数组循环创建),可以在父组件中通过 v-for 动态绑定插槽名称,子组件则遍历 context.slots() 来分发。

1. 父组件动态传递插槽

xml 复制代码
<template>
  <DynamicFunctionalSlot>
    <!-- 动态生成3个插槽:item-0、item-1、item-2 -->
    <template v-for="(item, index) in items" :slot="`item-${index}`">
      <div>动态插槽 {{ index }}:{{ item.content }}</div>
    </template>
  </DynamicFunctionalSlot>
</template>

<script>
export default {
  data() {
    return {
      items: [
        { content: '内容A' },
        { content: '内容B' },
        { content: '内容C' }
      ]
    };
  }
};
</script>

2. 子组件遍历动态插槽

javascript 复制代码
// DynamicFunctionalSlot.js
export default {
  functional: true,
  render(h, context) {
    const { slots } = context;
    const slotKeys = Object.keys(slots()); // 获取所有插槽名称
    
    return h('div', [
      // 遍历所有插槽并渲染
      slotKeys.map(key => 
        h('div', { class: `slot-item slot-${key}` }, slots()[key])
      )
    ]);
  }
};

四、关键API:context.slots()

在函数式组件中,context.slots() 是处理插槽的核心方法:

  • slots() 返回值:一个对象,键为插槽名称(如 header、default),值为插槽内容的虚拟DOM数组。
  • 动态插槽名称:通过 props 或外部数据动态指定插槽名称(如 slots()dynamicName)。
  • 默认插槽:slots().default 对应父组件未指定名称的内容。

五、注意事项

  1. 函数式组件无实例:无法使用 this.$slots,必须通过 context.slots() 获取插槽。
  2. 插槽名称大小写:Vue 2 中插槽名称不区分大小写,但建议统一使用小写(如 slot-a 而非 slotA)。
  3. 性能优化:函数式组件本身已足够轻量,动态插槽不会带来明显性能问题,但避免过度复杂的插槽逻辑。

六、完整示例代码

父组件

xml 复制代码
<template>
  <div>
    <FunctionalDynamicSlots :active-slot="activeSlot">
      <template slot="header">
        <h2>固定头部</h2>
      </template>
      <template slot="content-1">
        <p>动态内容 1</p>
      </template>
      <template slot="content-2">
        <p>动态内容 2</p>
      </template>
      <template slot="footer">
        <p>固定底部</p>
      </template>
    </FunctionalDynamicSlots>
    
    <button @click="activeSlot = 'content-1'">显示内容1</button>
    <button @click="activeSlot = 'content-2'">显示内容2</button>
  </div>
</template>

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

export default {
  components: { FunctionalDynamicSlots },
  data() {
    return {
      activeSlot: 'content-1'
    };
  }
};
</script>

函数式子组件

arduino 复制代码
// FunctionalDynamicSlots.js
export default {
  functional: true,
  props: {
    activeSlot: {
      type: String,
      required: true
    }
  },
  render(h, context) {
    const { props, slots } = context;
    const activeContent = slots()[props.activeSlot] || slots().default;

    return h('div', { class: 'dynamic-slot-container' }, [
      slots().header,
      h('div', { class: 'active-content' }, activeContent),
      slots().footer
    ]);
  }
};

通过以上方案,你可以在 Vue 2 函数式组件中灵活实现动态插槽,满足复杂的内容分发需求。如果需要进一步扩展(如作用域插槽),可以结合 context.data.scopedSlots 处理,但动态插槽本身已覆盖大部分场景。

相关推荐
码视野15 小时前
基于 Spring Boot + Vue3 的【城市道路地下空洞塌陷微动传感监测与路面脱空沉降预警中台】设计与实现(含PRD/三端高保真源码/大屏)
vue.js·spring boot·后端
雪芽蓝域zzs15 小时前
第三节:目录结构重构(仿若依,优化层级,layout 与 components 同级)
前端·vue.js·重构
子非鱼a16 小时前
【WEB】[NewStarCTF 公开赛赛道]UnserializeOne
前端·javascript·html
南雨北斗16 小时前
Vue3 v-model中的自动事件绑定
前端
雪芽蓝域zzs18 小时前
第八节:Element Plus 基础、封装通用组件 + 路由守卫
前端·vue.js
码视野18 小时前
基于 Spring Boot + Vue3 的【智慧公厕微负压排风除臭与客流人感空间占用导引中台】设计与实现(含PRD/三端高保真源码/大屏)
前端·vue.js·人工智能·spring boot·后端
计算机魔术师18 小时前
刚赔了15亿又来被告?索尼华纳联手把Anthropic告上法庭
前端
笑霸final18 小时前
不用上传服务器!用 Vue3 + ONNX Runtime Web 在浏览器本地实现智能抠图
运维·前端·图像处理·ai·onnx·canva可画·web性能优化
月月大王的3D日记18 小时前
Three.js 入门系列(9):从零搭一座“闹鬼小屋”
前端·javascript