您好,登录后才能下订单哦!
本篇内容介绍了“Vue.slot原理及slot是如何实现的”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
slot
用法<!-- 子组件 --> <template> <div class="wrapper"> <!-- 默认插槽 --> <div class="main"> <slot></slot> </div> </template> <!-- 父组件 --> <my-slot> <template> <h2>默认插槽</h2> </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> <h2>header 具名插槽</h2> </template> <template> <h2>默认插槽</h2> </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> <h2>header 具名插槽</h2> </template> <template> <h2>默认插槽</h2> </template> <template v-slot:footer="slotProps"> <h2>footer 具名 + 作用域插槽</h2> <p>{{ slotProps.footerInfo.text }}</p> </template> </my-slot>
如上代码块可以发现:
子组件中的 slot标签
除了有 name=footer
的属性,还有一个:footerInfo="footerInfo"
的属性(作用就是传递子组件数据)
父组件中的 template标签
不仅有 v-slot:footer
,并且还有一个赋值操作 ="slotProps"
,在模版的双括号语法中,直接通过 slotProps
访问到 子组件的 footerInfo
页面展示效果如图:
好了,简单回顾完用法后,笔者在这里先提三个问题:
普通插槽、 作用域插槽 的 vNode 是在哪个环节生成的,render 父组件时还是子组件时?
作用域插槽 为什么能在父组件访问到子组件的数据?
普通插槽 跟 作用域插槽 在实现上有区别吗?
我们带着疑问接着往下看!
slot
的编译区别我们根据上述最终的案例代码,执行一下打包命令,看看 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
两种具名插槽写法的区别。
v2.6 中,我们使用了具名插槽,但是又未使用 作用域插槽的 header 也被放在了 scopedSlots
,但是函数的参数为空,这点跟作用域插槽有区别。
v2.5 中, 具名插槽header 仅仅作为 my-slot组件
的children节点,并且其render函数的第二个参数中有一个 slot
的属性。
其实根据上述编译后的结果,我们不妨这样猜测:
默认插槽直接在父组件的 render
阶段生成 vNode
。
子组件 render
时,可能通过某种手段取得已经生成好的 插槽vNode
用作自己的 slot
节点。
因为观察上述默认插槽的render函数:e("h2", [t._v("默认插槽")])
,直接就是 my-slot
组件的childre节点(位于 my-slot
组件的第三个参数)。
作用域插槽是在子组件 render
阶段生成 vNode
。
因为我们观察作用域插槽 footer
的编译后结果,其不作为 my-slot
组件的 children,而是被放在了 my-slot
组件的 第二个参数 data
中的一个 scopedSlots属性
里。
并且,作用域插槽的 render 函数 外层的 funciton 接收了一个参数。如果在执行子组件 render 的时候调用,那完全可以拿到子组件的数据。
这里放出具体的 作用域插槽 打包后代码,大家一看就很清晰了:
{ scopedSlots: t._u([ { key: "footer", // 函数接收了一个参数n fn: function (n) { return [ // h2 标签的 render 函数 e("h2", [t._v("footer 具名 + 作用域插槽")]), // p 标签的 render 函数,这里可以看到编译后是:n.footerInfo.text e("p", [t._v(t._s(n.footerInfo.text))]) ] } } ]) }
slot
实现原理为了方便大家看调试结果,当前项目的组件结构主要是这样,有三大层:
Vue
-><App />
-><my-slot />
这里笔者在运行时代码 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组件
的 初始化流程。】
我们不难发现,图中此时正值 my-slot组件
的 init
阶段。
再往下执行,我们可以得到 App组件中的 <h2>默认插槽</h2>
的vNode,赋值给 vm.$slot
(这里我们记住,默认插槽的 vNode 已经得到)
2、再是进入 renderSlot()
。接着上面继续单步执行,会走到 renderSlot
中。这时候,已经进入到 my-slot组件
的 render
阶段了。回顾第一步中,此时我们手握 默认插槽的vNode,并存在 vm.$slot.default
中
header插槽
按顺序走,先是 render 排第一的 header 的 vNode。如图所示,会走到断点处,我们接着单步
直接进入执行我们 header插槽 的render函数执行处。根据调试步骤,我们可以肯定,放置在 scopedSlots属性
中的 render函数,是在子组件 render 的时候执行
得到 header插槽 的 vNode
默认插槽
继续单步走,这次轮到 默认插槽 了!如图所示,这里的 key
正是 'default'
。可以发现,这里并没有像上面 header插槽 一样,去执行 render,而是直接将我们之前得到的 插槽vNode返回了。
得到 default插槽 的 vNode
作用域插槽
前面都跟 header插槽 一致,都是会在 my-slot 组件中 执行插槽的 render。我们直接单步到 render 处看看有什么区别。这里可以得出,function处传入的参数正是我们子组件 my-slot
的 data
数据,这就是为什么我们在 App组件
能通过 作用域插槽
访问到子组件数据的原因了
最后也是返回 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、作用域插槽 为什么能在父组件访问到子组件的数据?
作用域插槽只有子组件render的时候,才会执行render生成vNode。并且,作用域插槽的 render 函数能接参数,从而获得子组件的数据。就是这样形成了作用域插槽!所以我们能在父组件中,访问到子组件的data数据。
3、普通插槽 跟 作用域插槽 在实现上有区别吗?
有区别。
普通插槽。如果是 v2.5
,具名插槽 和 默认插槽 都只在 父组件 render 的时候生成 vNode,子组件要 渲染slot 的时候,直接在父组件实例的 $slot 中获取已经是vNode的数据。
普通插槽。如果是 v2.6
,具名插槽 虽然是在子组件中执行的 render,但是其不接收参数。
作用域插槽。不管 v2.5
还是 v2.6
,都只在 子组件执行 render,并且能接收参数。
“Vue.slot原理及slot是如何实现的”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。