花3分钟来了解一下Vue3中的插槽到底是什么玩意

news/2025/3/5 9:38:44/文章来源:https://www.cnblogs.com/heavenYJJ/p/18750915

前言

插槽看着是一个比较神秘的东西,特别是作用域插槽还能让我们在父组件里面直接访问子组件里面的数据,这让插槽变得更加神秘了。其实Vue3的插槽远比你想象的简单,这篇文章我们来揭开插槽的神秘面纱。

欧阳也在找工作,坐标成都求内推!

看个demo

我们先来看个常见的插槽demo,其中子组件代码如下:

<template><slot></slot><slot name="header"></slot><slot name="footer" :desc="desc"></slot>
</template><script setup>
import { ref } from "vue";
const desc = ref("footer desc");
</script>

在子组件中我们定义了三个插槽,第一个是默认插槽,第二个是name为header的插槽,第三个是name为footer的插槽,并且将desc变量传递给了父组件。

我们再来看看父组件代码如下:

<template><ChildDemo><p>default slot</p><template v-slot:header><p>header slot</p></template><template v-slot:footer="{ desc }"><p>footer slot: {{ desc }}</p></template></ChildDemo>
</template><script setup lang="ts">
import ChildDemo from "./child.vue";
</script>

在父组件中的代码很常规,分别使用v-slot指令给headerfooter插槽传递内容。

来看看编译后的父组件

我们在浏览器中来看看编译后的父组件代码,简化后如下:

import {createBlock as _createBlock,createElementVNode as _createElementVNode,openBlock as _openBlock,toDisplayString as _toDisplayString,withCtx as _withCtx,
} from "/node_modules/.vite/deps/vue.js?v=64ab5d5e";const _sfc_main = /* @__PURE__ */ _defineComponent({// ...省略
});function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(),_createBlock($setup["ChildDemo"], null, {header: _withCtx(() =>_cache[0] ||(_cache[0] = [_createElementVNode("p",null,"header slot",-1/* HOISTED */),])),footer: _withCtx(({ desc }) => [_createElementVNode("p",null,"footer slot: " + _toDisplayString(desc),1/* TEXT */),]),default: _withCtx(() => [_cache[1] ||(_cache[1] = _createElementVNode("p",null,"default slot",-1/* HOISTED */)),]),_: 1,/* STABLE */}));
}
export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render],
]);

从上面的代码可以看到template中的代码编译后变成了render函数。

在render函数中_createBlock($setup["ChildDemo"]表示在渲染子组件ChildDemo,并且在执行createBlock函数时传入了第三个参数是一个对象。对象中包含headerfooterdefault三个方法,这三个方法对应的是子组件ChildDemo`中的三个插槽。执行这三个方法就会生成这三个插槽对应的虚拟DOM。

并且我们观察到插槽footer处的方法还接收一个对象作为参数,并且对象中还有一个desc字段,这个字段就是子组件传递给父组件的变量。

方法最外层的withCtx方法是为了给插槽的方法注入当前组件实例的上下文。

通过上面的分析我们可以得出一个结论:在父组件中插槽经过编译后会变成一堆由插槽name组成的方法,执行这些方法就会生成插槽对应的虚拟DOM。默认插槽就是default方法,方法接收的参数就是子组件中插槽给父组件传递的变量。但是有一点要注意,在父组件中我们只是定义了这三个方法,执行这三个方法的地方却不是在父组件,而是在子组件。

编译后的子组件

我们来看看编译后的子组件,简化后代码如下:

import {createElementBlock as _createElementBlock,Fragment as _Fragment,openBlock as _openBlock,renderSlot as _renderSlot,
} from "/node_modules/.vite/deps/vue.js?v=64ab5d5e";const _sfc_main = {// ...省略
};function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(),_createElementBlock(_Fragment,null,[_renderSlot(_ctx.$slots, "default"),_renderSlot(_ctx.$slots, "header"),_renderSlot(_ctx.$slots, "footer", { desc: $setup.desc }),],64 /* STABLE_FRAGMENT */));
}export default /*#__PURE__*/ _export_sfc(_sfc_main, [["render", _sfc_render]]);

同样的我们观察里面的render函数,里面的这个:

[_renderSlot(_ctx.$slots, "default"),_renderSlot(_ctx.$slots, "header"),_renderSlot(_ctx.$slots, "footer", { desc: $setup.desc }),
]

对应的就是源代码里面的这个:

<slot></slot>
<slot name="header"></slot>
<slot name="footer" :desc="desc"></slot>

在上面我们看见一个$slots对象,这个是什么东西呢?

其实useSlots就是返回的$slots对象,我们直接在控制台中使用useSlots打印出$slots对象看看,代码如下:

<script setup>
import { ref, useSlots } from "vue";const slots = useSlots();
console.log(slots);
const desc = ref("footer desc");
</script>

我们来浏览器中看看此时打印的slots对象是什么样的,如下图:
slots

从上图中可以看到slots对象好像有点熟悉,这个对象中包含defaultfooterheader这三个方法,其实这个slots对象就是前面我们讲的父组件中定义的那个对象,执行对象的defaultfooterheader方法就会生成对应插槽的虚拟DOM。

前面我们讲了在父组件中会定义defaultfooterheader这三个方法,那这三个方法又是在哪里执行的呢?

答案是:在子组件里面执行的。

在执行_renderSlot(_ctx.$slots, "default")方法时就会去执行slots对象里面的default方法,这个是renderSlot函数的代码截图:
renderSlot

从上图中可以看到在renderSlot函数中首先会使用slots[name]拿到对应的插槽方法,如果执行的是_renderSlot(_ctx.$slots, "footer", { desc: $setup.desc }),这里拿到的就是footer方法。

然后就是执行footer方法,前面我们讲过了这里的footer方法需要接收参数,并且从参数中结构出desc属性。刚好我们执行renderSlot方法时就给他传了一个对象,对象中就有一个desc属性,这不就对上了吗!

并且由于执行footer方法会生成虚拟DOM,所以footer生成的虚拟DOM是属于子组件里面的,同理footer对应的真实DOM也是属于在子组件的DOM树里面。

通过上面的分析我们可以得出一个结论就是:子组件中的插槽实际就是在执行父组件插槽对应的方法,在执行方法时可以将子组件的变量传递给父组件,这就是作用域插槽的原理。

总结

这篇文章我们讲了经过编译后父组件的插槽会被编译成一堆方法,这些方法组成的对象就是$slots对象。在子组件中会去执行这些方法,并且可以将子组件的变量传给父组件,由父组件去接收参数,这就是作用域插槽的原理。了解了这个后当我们在useSlotsjsxtsx中定义和使用插槽就不会那么迷茫了。

关注公众号:【前端欧阳】,给自己一个进阶vue的机会

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/893886.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OKR 推行五大注意事项

OKR(目标与关键成果法)是一种高效的目标管理工具,但在推行过程中往往会面临诸多挑战。以下是OKR推行的五大难点,以及在推行过程中需要注意的五个关键事项,并结合Tita平台的特点进行简要介绍。OKR推行的五大难点目标设定不合理确定清晰、合理且可衡量的目标和关键成果是推行…

注册用户同步没有了

解决: https://vip.kingdee.com/knowledge/642363720735104256?productLineId=1&isKnowledge=2&lang=zh-CN

同步注册用户没有了

解决: https://vip.kingdee.com/knowledge/642363720735104256?productLineId=1&isKnowledge=2&lang=zh-CN

2025.03.04 CW 模拟赛 D. 积木

D. 积木 和之前容斥专题的一道题有点像. 思路 注意到虽然 \(1 \le n, m \le 50\), 但是 * 的个数不超过 12 个. 于是我们可以考虑对 * 的个数进行状压, 也就是钦定哪些 * 必须作为积木的中心 \((\)下文统称为 o\()\). 钦定完成, 我们考虑什么情况下是不合法的. 如下图, 这两种情…

9.0版本要求使用https登录管理中心

解决:如下图 https://vip.kingdee.com/knowledge/552811796516494592?productLineId=1&isKnowledge=2&lang=zh-CN

西门子 smart 700 人机界面 HMI erwa.cn二娃备忘

情况是这样,触摸屏型号为smart 700IE,以前的程序已经确定丢失了,已经是两年前其他人搞的程序了。目前想要将此smart 700IE触摸屏程序得出来,该怎么实现,请详细告知,非常感谢 最佳答案 Smart Panels 均不支持“回传”功能,只支持“备份”“恢复”功能。 那“回传”与“…

文件同步备份软件,让文件同步更安全更稳定!

在数字化时代,数据已然成为企业的核心资产,而确保服务器之间文件的高效同步,是保障数据一致性、维持业务连续性的关键所在。无论是大型企业构建多数据中心的复杂架构,还是分布式系统力求各节点数据的统一,文件同步备份软件都发挥着无可替代的作用。它就像是企业数据流转的…

无法加载kingdee k3 cloud erp V7.2的许可文件,许可文件可能被篡改,请联系管理员

原因:如下图,正式补丁在安装时出现了异常导致。 解决:重新安装正式补丁以及临时补丁。

2025年最火爆的5款跨网文件安全交换系统优缺点对比

跨网文件安全交换系统主要用于在不同网络环境间安全传输文件,确保数据的机密性、完整性和可用性。 能解决跨网文件传输的方式有很多,比如FTP/FTPS、SFTP、云存储服务、P2P文件共享以及Ftrans Ferry跨网文件安全交换系统各有优缺点。以下是它们的详细对比: 一、FTP/FTPS优点:…

计算两个经纬点的距离

php cookbook有一个计算两个经纬点的例子,代码就照着抄了一遍,多次检查,多次运行的结果都是略小于预期值的一半,楞是费了一番,才发现是经纬度参数的顺序反了,在英文里,是纬度在前,经度在后,不似耳熟的东经多少多少,北纬多少多少。既然到了这了,也学着梳理一下计算经…

如何破局大文件远程跨国传输难题,实现业务高增长?

在全球化浪潮中,跨国合作已成为众多行业的常态。无论是医疗影像、基建数据、影视制作,还是跨国企业的日常运营,大文件远程跨国传输的需求日益增长。然而,网络延迟、带宽限制、数据安全等问题,常常让这一过程变得低效且充满风险。如何实现高效、安全、稳定的大文件远程跨国…

2023陇剑杯

数据分析-SS 1.黑客是使用什么漏洞来拿下root权限的 分析使用系统命令id之前的流量包,发现奇怪的传输上网搜索发现是CVE-2022-22965 2.黑客反弹shell的ip和端口是什么 分析后面的流量包,查看返回包明文192.168.43.128:2333 3.黑客的病毒名称是什么? 解压文件系统按照创建时间…