Vue2、Vue3中的$scopedSlots和$slots区别

$scopedSlots(作用域插槽)

定义:子组件提供数据,父组件决定如何渲染,数据作用域属于子组件。

本质:子组件不直接渲染内容,而是接收一个函数,这个函数在子组件作用域内执行,从而让父组件的模板可以访问子组件的数据。

js 复制代码
// 作用域插槽的本质:一个函数,子组件调用时传入数据
this.$scopedSlots.default = function(data) {
  // 这个函数在父组件的作用域编译
  // 但参数 data 来自子组件
  return VNode  // 返回渲染好的节点
}

$slots(普通插槽)

定义:父组件提供内容,子组件决定在哪里渲染,数据作用域属于父组件。

本质:父组件在编译时就已经确定了插槽内容的所有数据和逻辑,子组件只是作为一个容器来摆放这些内容。

js 复制代码
// 普通插槽的本质:父组件编译好的 VNode 数组
// 子组件只是被动接收
this.$slots.default = [VNode, VNode, ...]  // 已经是渲染好的节点

数据作用域的指向

html 复制代码
<!-- 父组件 Parent.vue -->
<template>
  <div>
    <!-- 普通插槽 -->
    <ChildComponent>
      <div>{{ parentMessage }}</div>  <!-- ✅ 可以访问父组件数据 -->
      <!-- <div>{{ childMessage }}</div>  ❌ 不能访问子组件数据 -->
    </ChildComponent>
    
    <!-- 作用域插槽 -->
    <ChildComponent>
      <template v-slot:default="slotProps">
        <div>{{ parentMessage }}</div>  <!-- ✅ 可以访问父组件数据 -->
        <div>{{ slotProps.childMessage }}</div>  <!-- ✅ 可以访问子组件数据 -->
      </template>
    </ChildComponent>
  </div>
</template>

<script>
export default {
  data() {
    return {
      parentMessage: '父组件的数据'  // 父组件作用域
    }
  }
}
</script>
html 复制代码
<!-- 子组件 ChildComponent.vue -->
<template>
  <div>
    <!-- 普通插槽:直接渲染父组件传来的内容 -->
    <slot></slot>
    
    <!-- 作用域插槽:将子组件数据传递给父组件 -->
    <slot :childMessage="childMessage"></slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      childMessage: '子组件的数据'  // 子组件作用域
    }
  }
}
</script>

编译时 vs 运行时

普通插槽:编译时确定

js 复制代码
// 父组件模板
<template>
  <child>
    <span>{{ message }}</span>  <!-- message 在编译时就绑定到父组件 -->
  </child>
</template>

// 编译后的渲染函数(简化)
render() {
  // 在父组件作用域中创建 VNode
  const children = [createVNode('span', null, this.message)]
  
  // 传递给子组件
  return h(Child, null, { default: () => children })
}

作用域插槽:运行时确定

js 复制代码
// 父组件模板
<template>
  <child>
    <template v-slot="props">
      <span>{{ props.message }}</span>  <!-- message 来自子组件 -->
    </template>
  </child>
</template>

// 编译后的渲染函数(简化)
render() {
  // 父组件不直接创建 VNode,而是创建一个函数
  const scopedSlotFn = (props) => {
    return createVNode('span', null, props.message)
  }
  
  // 把这个函数传递给子组件
  return h(Child, null, { default: scopedSlotFn })
}

// 子组件中
render() {
  // 子组件调用这个函数,传入自己的数据
  const vnode = this.$scopedSlots.default({ message: this.childMessage })
  return vnode
}

总结对比表

维度 普通插槽 作用域插槽
数据来源 父组件 子组件
存储形式 VNode数组 函数
编译时机 父组件编译时 子组件运行时调用
使用场景 布局、内容填充 自定义渲染,列表渲染
灵活性 低(内容固定) 高(可动态渲染)
数据流向 父 → 子(仅传递内容) 子 → 父 (仅传递数据)

vue3变化, <math xmlns="http://www.w3.org/1998/Math/MathML"> s c o p e d S l o t s 被移除,统一使用 scopedSlots被移除,统一使用 </math>scopedSlots被移除,统一使用slots,所有插槽都是函数。

html 复制代码
<!-- 子组件 Child.vue -->
<template>
  <div>
    <!-- 普通插槽 -->
    <slot></slot>
    
    <!-- 作用域插槽 -->
    <slot name="item" :data="itemData"></slot>
  </div>
</template>

<script setup>
import { onMounted } from 'vue'

const itemData = { name: 'Vue 3', version: 3 }

onMounted(() => {
  // Vue 3 中,所有插槽都是函数
  console.log(typeof $slots.default)  // 'function'
  console.log(typeof $slots.item)     // 'function'
  
  // 调用函数获取 VNode
  const defaultVNode = $slots.default()
  const itemVNode = $slots.item({ data: itemData })
  
  // 注意:Vue 3 中 $slots 返回的是 VNode 数组
  console.log(Array.isArray(defaultVNode))  // true
})
</script>
特性 Vue 2 Vue 3
普通插槽存储 $slots (VNode 数组) $slots (函数)
作用域插槽存储 $scopedSlots (函数) $slots (函数)
模板语法 slot + slot-scope 统一 v-slot#
访问方式 this.$slots / this.$scopedSlots useSlots()$slots
类型判断 Array.isArray($slots.default) typeof $slots.default === 'function'
调用方式 普通插槽直接使用,作用域插槽需调用 所有插槽都需调用
相关推荐
.Cnn2 小时前
Ajax与Vue 生命周期核心笔记
前端·javascript·vue.js·笔记·ajax
吴声子夜歌3 小时前
Vue3——渲染函数
前端·vue.js·vue·es6
Ruihong3 小时前
你的 Vue KeepAlive 组件,VuReact 会编译成什么样的 React 代码?
vue.js·react.js·面试
Ruihong3 小时前
你的 Vue slot 插槽,VuReact 会编译成什么样的 React 代码?
vue.js·react.js·面试
一 乐3 小时前
房产租赁管理|基于springboot + vue房产租赁管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·房产租赁管理系统
2501_913680004 小时前
Vue3项目快速接入AI助手的终极方案 - 让你的应用智能升级
前端·vue.js·人工智能·ai·vue·开源软件
踩着两条虫4 小时前
VTJ:应用场景展示
前端·vue.js·架构
Ruihong4 小时前
Vue v-slot → 用 VuReact 转换后变成这样的 React 代码
vue.js·react.js·面试
Ruihong4 小时前
你的 Vue v-model,VuReact 会编译成什么样的 React 代码?
vue.js·react.js·面试
Cobyte5 小时前
6.响应式系统比对:通过 Vue3 响应式库写 React 应用
前端·javascript·vue.js