本篇文章给大家分享一下vue干货,聊聊你可能不知道的vue.slot原理,希望对大家有所帮助!
相信不管是日常业务开发,还是封装基础组件,插槽slot 都是经常出现在我们的视野的,因为它为我们编程实现提供了很多便捷。可能大家对于 slot 的用法已经烂透于心了,不管是 具名插槽 ,还是 作用域插槽 各种用法等等...那大?们又知不知道 slot、slot-scope 底层是怎么实现的呢?
通俗易懂、10分钟就能带走的 Vue.slot 的干货源码实现分析!!!跟着笔者一起探究下 Vue(v2.6.14) 中的插槽 slot 是怎么实现的!!本文主要会分两块进行讲解:
普通插槽(具名插槽、默认插槽)
作用域插槽
立即学习“前端免费学习笔记(深入)”;
这篇文章没有晦涩的源码解析,直接用大白话讲解,所以不管大家对Vue源码的熟悉程度,都是能看明白的。通过现场调试,让你看清 Vue 的 slot 是如何实现的。let's go go go!(学习视频分享:vue视频教程)
先跟大家一起回顾下插槽的大概用法。这里的 slot 用法使用 2.6.0 的新标准(本文也会带一下 v2.5 的写法的跟 v2.6 在源码实现上有什么区别!)。
如果想详细了解用法可以去官网详细看看Vue 的 slot 文档
https://cn.vuejs.org/v2/guide/components-slots.html
<!-- 子组件 --> <template> <div class="wrapper"> <!-- 默认插槽 --> <div class="main"> <slot></slot> </div> </template> <!-- 父组件 --> <my-slot> <template> <h1>默认插槽</h1> </template> </my-slot>
页面展示效果如图:
接着上述的案例,添加具名插槽 header ,代码如下:
<!-- 子组件 --> <template> <div class="wrapper"> <!-- header 具名插槽 --> <header class="header"> <slot name="header"></slot> </header> <!-- 默认插槽 --> <div class="main"> <slot></slot> </div> </template> <!-- 父组件 --> <my-slot> <template v-slot:header> <h1>header 具名插槽</h1> </template> <template> <h1>默认插槽</h1> </template> </my-slot>
如上代码块可以发现:
子组件中的 slot标签 带上了一个名为 name 的属性,值为 header
父组件中的 template标签 带上了 v-slot 的属性,值为 header
页面展示效果如图:
再接着上述案例,添加作用域插槽 footer ,代码如下
<!-- 子组件 --> <template> <div class="wrapper"> <!-- header 具名插槽 --> <header class="header"> <slot name="header"></slot> </header> <!-- 默认插槽 --> <div class="main"> <slot></slot> </div> <!-- footer 具名 + 作用域插槽 --> <footer class="footer"> <slot name="footer" :footerInfo="footerInfo"></slot> </footer> </div> </template> <script> export default { name: "mySlot", data () { return { footerInfo: { text: '这是 子组件 footer插槽 的作用域数据' } } } } </script> <!-- 父组件 --> <my-slot> <template v-slot:header> <h1>header 具名插槽</h1> </template> <template> <h1>默认插槽</h1> </template> <template v-slot:footer="slotProps"> <h1>footer 具名 + 作用域插槽</h1> <p>{{ slotProps.footerInfo.text }}</p> </template> </my-slot>
如上代码块可以发现:
子组件中的 slot标签 除了有 name=footer 的属性,还有一个:footerInfo="footerInfo" 的属性(作用就是传递子组件数据)
父组件中的 template标签 不仅有 v-slot:footer ,并且还有一个赋值操作 ="slotProps",在模版的双括号语法中,直接通过 slotProps 访问到 子组件的 footerInfo
页面展示效果如图:
好了,简单回顾完用法后,笔者在这里先提三个问题:
我们带着疑问接着往下看!
我们根据上述最终的案例代码,执行一下打包命令,看看 Vue 在编译模板的时候,是怎么处理我们的 slot 的!事不宜迟,赶紧 build 一哈~(偷偷告诉大?,Vue 处理 作用域插槽 跟 普通插槽 的差异就是从编译开始的,也就是 render函数 会有所不同)
这里笔者顺便使用 v2.5 的具名插槽写法给大?参照一下(对具名插槽header做改写,使用 slot="header" 的写法),大家可以看下 v2.6、v2.5 具名插槽的 写法、实现 上的区别~反正也不难,也就顺便带出来看看了
上图左边是 v2.6 、右边是 v2.5 的,这里,我们集中关注:
scopedSlots 属性。使用作用域插槽的 footer 的 render函数 是放在 scopedSlots 里的,并且 函数中 还有接收一个参数
my-slot 的 children。可以发现,默认插槽的 render函数 一直都是作为当前组件的childre节点,放在 当前 组件render函数 的第三个参数中
关注 header 两种具名插槽写法的区别。
其实根据上述编译后的结果,我们不妨这样猜测:
默认插槽直接在父组件的 render 阶段生成 vNode。
作用域插槽是在子组件 render 阶段生成 vNode。
这里放出具体的 作用域插槽 打包后代码,大家一看就很清晰了:
{ scopedSlots: t._u([ { key: "footer", // 函数接收了一个参数n fn: function (n) { return [ // h1 标签的 render 函数 e("h1", [t._v("footer 具名 + 作用域插槽")]), // p 标签的 render 函数,这里可以看到编译后是:n.footerInfo.text e("p", [t._v(t._s(n.footerInfo.text))]) ] } } ]) }
为了方便大家看调试结果,当前项目的组件结构主要是这样,有三大层:
Vue ->
->
这里笔者在运行时代码 initRender()、renderSlot() 中,打上 debugger ,直接带大火看看执行流程。这里简单介绍下两个方法:
initRender:获取 vm.$slot 。组件初始化时执行(比如执行到 my-slot组件 时,可从vm.$slot 获取父组件中的slot vNode,也就是我们的 默认插槽)
renderSlot:把组件的 slot 替换成真正的 插槽vNode
接下来直接看实验截图:
1、先是进入initRender()(这里跳过初始化 大Vue、App 的过程)。直接到初始化 my-slot组件 过程。【 简单解释:由于 App组件 执行 render 得到 App组件vNode ,在 patch 过程中 遇到 vue-component-my-slot 的 vNode ,又执行 my-slot组件 的 初始化流程。不是很熟悉组件化流程的朋友可以去看看笔者的Vue响应式原理~】
2、再是进入 renderSlot()。接着上面继续单步执行,会走到 renderSlot 中。这时候,已经进入到 my-slot组件 的 render 阶段了。回顾第一步中,此时我们手握 默认插槽的vNode,并存在 vm.$slot.default 中
header插槽
默认插槽
作用域插槽
最后也是返回 footer插槽 的vNode。好了,验证过程结束~
其实上面的流程只是论证过程,大家不可以不必深陷其中。笔者在这里直接根据实践过程,给大伙总结出结论!也就是要回到我们一开始的三个问题!
1、普通插槽、 作用域插槽 的 vNode 是在哪个环节生成的,render 父组件时还是子组件时?
默认插槽,不管 v2.5 、 v2.6 的写法,都是在 父组件中生成 vNode。vNode 存在 vm.$slot 中。待子组件 render 到插槽时,会直接拿到 父组件的 vNode
具名插槽两个版本情况不一。根据编译结果可知:
v2.5 的写法,跟默认插槽是一样的,在父组件生成vNode,子组件直接拿来用
v2.6 中,直接时在 子组件 中才去执行 插槽render ,生成 插槽vNode。
作用域插槽。不管版本,都是在子组件中进行render的。
大家不妨这么理解,模版编译后,只要是被放在 scopeSlots属性 中的插槽,都会在子组件执行 render 的时候才会去生成vNode。
2、作用域插槽 为什么能在父组件访问到子组件的数据?
3、普通插槽 跟 作用域插槽 在实现上有区别吗?
普通插槽。如果是 v2.5 ,具名插槽 和 默认插槽 都只在 父组件 render 的时候生成 vNode,子组件要 渲染slot 的时候,直接在父组件实例的 $slot 中获取已经是vNode的数据。
普通插槽。如果是 v2.6 ,具名插槽 虽然是在子组件中执行的 render,但是其不接收参数。
作用域插槽。不管 v2.5 还是 v2.6,都只在 子组件执行 render,并且能接收参数。
好了,最后来个精炼的总结。作用域插槽一定是延迟执行,且接收参数!普通插槽 可能延迟执行,可能直接执行,但不接收参数!
写在最后,很多时候我们搬砖,遵照文档把功能实现确实省力省心~但当你做多了,你就发现当前的东西缺乏挑战,索然无味。那这个时候,就会有一种冲动,想深入其实现原理,看看 slot 到底是怎么实现的。特别是作用域插槽。用的时候都会想当然的觉得在上层组件通过作用域插槽拿到子组件的数据理所应当,但是在深入源码之后,看懂了别人是怎么做的,会有突然恍然大悟的感觉~
以上就是聊聊Vue.slot原理,起探究下slot 是怎么实现的!的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号