이 글은 Vue에 대한 유용한 정보를 공유하고, 여러분이 알지 못하는 Vue.slot의 원리에 대해 이야기해 보는 것이 모든 사람에게 도움이 되기를 바랍니다.
일상적인 비즈니스 개발이든 기본 구성 요소의 캡슐화이든
슬롯
은 프로그래밍 구현에 많은 편의를 제공하기 때문에 우리 시야에 자주 등장한다고 믿습니다. 이름이 지정된 슬롯이든 스코프 슬롯의 다양한 용도 등슬롯
의 사용법은 모두가 잘 알고 있을 것입니다.slot
및slot-scope
의 하위 레이어가 어떻게 구현되는지 알고 계시나요?插槽slot
都是经常出现在我们的视野的,因为它为我们编程实现提供了很多便捷。可能大家对于slot
的用法已经烂透于心了,不管是 具名插槽 ,还是 作用域插槽 各种用法等等...那大?们又知不知道slot
、slot-scope
底层是怎么实现的呢?
通俗易懂、10分钟就能带走的 Vue.slot
的干货源码实现分析!!!跟着笔者一起探究下 Vue(v2.6.14)
中的插槽 slot
是怎么实现的!!本文主要会分两块进行讲解:
普通插槽(具名插槽、默认插槽)
作用域插槽
这篇文章没有晦涩的源码解析,直接用大白话讲解,所以不管大家对Vue源码的熟悉程度,都是能看明白的。通过现场调试,让你看清 Vue
的 slot
是如何实现的。let's go go go!(学习视频分享:vue视频教程)
slot
用法先跟大家一起回顾下插槽的大概用法。这里的 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
页面展示效果如图:
好了,简单回顾完用法后,笔者在这里先提三个问题:
我们带着疑问接着往下看!
slot
的编译区别我们根据上述最终的案例代码,执行一下打包命令,看看 Vue 在编译模板的时候,是怎么处理我们的 slot
的!事不宜迟,赶紧 build
一哈~(偷偷告诉大?,Vue
处理 作用域插槽 跟 普通插槽 的差异就是从编译开始的,也就是 render函数 会有所不同)
这里笔者顺便使用 v2.5
的具名插槽写法给大?参照一下(对具名插槽header做改写,使用 slot="header"
的写法),大家可以看下 v2.6
、v2.5
Vue(v2.6.14)
의 슬롯 slot
이 어떻게 구현되는지 살펴보세요! ! 이 글에서는 주로 두 부분으로 나누어 설명합니다: 🎜Vue
의 슬롯
이 어떻게 구현되었는지 명확하게 확인할 수 있습니다. 가자 가자 가자! (학습 영상 공유: vue video tutorial🎜)🎜슬롯
사용법 검토slot
의 사용은 2.6.0의 새로운 표준을 사용합니다(이 기사에서는 v2.5
및 v2.5
의 작성 방법에 대해서도 설명합니다. code>v2.6 소스코드 구현의 차이점은 무엇인가요! 🎜🎜🎜사용법에 대해 더 자세히 알고 싶다면 공식 웹사이트로 이동하여 Vue의 슬롯 문서를 자세히 읽어보세요🎜🎜https://cn.vuejs.org/v2/guide/comComponents-slots.html🎜 blockquote>{ 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))]) ] } } ]) }
header
라는 이름을 추가하면 코드는 다음과 같습니다. 🎜rrreee🎜위 코드 블록에서 찾을 수 있습니다: 🎜슬롯 태그
에 이름이 있습니다. name
속성에 header
🎜템플릿 값이 있습니다. 상위 구성 요소의 태그
에는 v -slot
속성이 있고 값은 header
🎜footer
를 추가합니다. 다음과 같습니다🎜rrreee🎜위의 코드 블록을 찾을 수 있습니다:🎜name=footer
속성 외에도 구성 요소에는 :footerInfo="footerInfo"
속성도 있습니다(해당 기능은 하위 구성 요소 데이터를 전달하는 것입니다)🎜템플릿 태그
상위 구성 요소에는 v-slot:footer
가 있을 뿐만 아니라 할당 작업 = "slotProps"
도 있습니다. 템플릿의 이중 대괄호 구문에서는 footerInfo
🎜slotProps
를 통한 하위 구성 요소 페이지 표시 효과는 그림과 같습니다: 🎜🎜🎜🎜자, 사용법을 간략히 살펴본 후 작성자는 여기서 세 가지 질문을 할 것입니다: 🎜슬롯
슬롯
을 어떻게 처리하나요? 더 이상 고민할 필요 없이 서둘러 빌드
하세요~ (모두에게 비밀리에 알리시겠습니까? Vue
는 스코프 슬롯 및 일반 슬롯을 처리합니다. 차이점은 컴파일부터 시작됩니다. 즉, 렌더링 기능이 달라집니다) 🎜🎜그런데 작성자는 참고용으로 v2.5
의 명명된 슬롯 쓰기 방법을 사용합니다( 명명된 슬롯 slot="header"
를 사용하여 슬롯 헤더를 다시 작성합니다. v2.6
, v2.5
명명된 슬롯을 살펴볼 수 있습니다. 작성과 구현의 차이~ 어차피 어렵지 않아서 그냥 꺼내서 살펴봤습니다🎜上图左边是 v2.6
、右边是 v2.5
的,这里,我们集中关注:
scopedSlots
属性。使用作用域插槽的 footer
的 render函数 是放在 scopedSlots
里的,并且 函数中 还有接收一个参数
my-slot
的 children
。可以发现,默认插槽的 render函数
一直都是作为当前组件的childre节点
,放在 当前 组件render函数 的第三个参数中
关注 header
两种具名插槽写法的区别。
scopedSlots
,但是函数的参数为空,这点跟作用域插槽有区别。my-slot组件
的children节点,并且其render函数的第二个参数中有一个 slot
的属性。其实根据上述编译后的结果,我们不妨这样猜测:
默认插槽直接在父组件的 render
阶段生成 vNode
。
render
时,可能通过某种手段取得已经生成好的 插槽vNode
用作自己的 slot
节点。e("h1", [t._v("默认插槽")])
,直接就是 my-slot
组件的childre节点(位于 my-slot
组件的第三个参数)。作用域插槽是在子组件 render
阶段生成 vNode
。
footer
的编译后结果,其不作为 my-slot
组件的 children,而是被放在了 my-slot
组件的 第二个参数 data
中的一个 scopedSlots属性
里。这里放出具体的 作用域插槽 打包后代码,大家一看就很清晰了:
{ 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))]) ] } } ]) }
slot
实现原理为了方便大家看调试结果,当前项目的组件结构主要是这样,有三大层:
Vue
-><app></app>
-><my-slot></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组件
的 初始化流程。不是很熟悉组件化流程的朋友可以去看看笔者的Vue响应式原理~】
my-slot组件
的 init
阶段。<h1>默认插槽</h1>
的vNode,赋值给 vm.$slot
(这里我们记住,默认插槽的 vNode 已经得到)2. 그런 다음 renderSlot()
을 입력하세요. 그런 다음 위의 단일 단계 실행을 계속하면 renderSlot
에 도달하게 됩니다. 현재 my-slot 컴포넌트
의 render
단계에 진입했습니다. 첫 번째 단계를 되돌아보면 현재 기본 슬롯의 vNode가 있고 vm.$slot.default
renderSlot()
。接着上面继续单步执行,会走到 renderSlot
中。这时候,已经进入到 my-slot组件
的 render
阶段了。回顾第一步中,此时我们手握 默认插槽的vNode,并存在 vm.$slot.default
中
header插槽
scopedSlots属性
中的 render函数,是在子组件 render 的时候执行
默认插槽
key
正是 'default'
。可以发现,这里并没有像上面 header插槽 一样,去执行 render,而是直接将我们之前得到的 插槽vNode返回了。
作用域插槽
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、作用域插槽 为什么能在父组件访问到子组件的数据?
3、普通插槽 跟 作用域插槽 在实现上有区别吗?
普通插槽。如果是 v2.5
scopedSlots 속성
에 배치된 렌더링 함수가 실행되는지 확인할 수 있습니다
키
는 정확히 '기본값'
입니다. 여기에서는 위의 헤더 슬롯처럼 렌더링을 실행하지 않고, 앞서 얻은 슬롯 vNode를 직접 반환하는 것을 확인할 수 있습니다.
my-slot
의 data
데이터라는 결론을 내릴 수 있습니다. 이것이 우리가 App 컴포넌트 scope 슬롯
을 통해 하위 컴포넌트 데이터에 접근할 수 있는 이유
v2.5
또는 v2.6
이 어떻게 작성되든 항상 상위 구성 요소 vNode
를 생성합니다. vNode
가 vm.$slot
에 존재합니다. 하위 구성요소 렌더링
이 슬롯에 도달하면 상위 구성요소의 vNode
🎜v2.5
는 기본 슬롯과 동일한 방식으로 작성되어 상위 구성 요소 vNode, 하위 구성 요소는 🎜v2.6
에서 직접 사용되며 슬롯 렌더링
은 하위 구성 요소에서 직접 실행되어 Slot vNode
를 생성합니다. 🎜v2.5
인 경우 명명된 슬롯과 기본 슬롯 모두 상위 구성 요소가 렌더링될 때만 vNode를 생성합니다. 하위 구성 요소가 슬롯을 렌더링하려는 경우 $slot에서 직접 가져옵니다. 상위 구성 요소 인스턴스입니다. vNode의 데이터입니다. 🎜일반 슬롯. v2.6
인 경우, 명명된 슬롯이 하위 컴포넌트에서 렌더링을 실행하더라도 매개변수를 받지 않습니다. v2.6
,具名插槽 虽然是在子组件中执行的 render,但是其不接收参数。
作用域插槽。不管 v2.5
还是 v2.6
,都只在 子组件执行 render,并且能接收参数。
好了,最后来个精炼的总结。作用域插槽一定是延迟执行,且接收参数!普通插槽 可能延迟执行,可能直接执行,但不接收参数!
写在最后,很多时候我们搬砖,遵照文档把功能实现确实省力省心~但当你做多了,你就发现当前的东西缺乏挑战,索然无味。那这个时候,就会有一种冲动,想深入其实现原理,看看 slot
v2.5
또는 v2.6
에 관계없이 렌더링은 하위 구성 요소에서만 실행되며 매개변수를 받을 수 있습니다.
슬롯
이 어떻게 구현되는지 살펴보고 싶은 충동이 생길 것입니다. 특히 🎜범위가 지정된 슬롯🎜. 사용하다 보면 상위 컴포넌트가 스코프 슬롯을 통해 하위 컴포넌트의 데이터를 얻어야 한다는 것을 당연하게 여기겠지만, 소스 코드를 파고들어 다른 사람들이 어떻게 하는지 이해하고 나면 문득 깨달음을 얻게 될 것입니다. ~🎜🎜( 학습 영상 공유: 🎜웹 프론트엔드 개발🎜, 🎜프로그래밍 기초 영상🎜)🎜위 내용은 Vue.slot의 원리에 대해 이야기하고 슬롯이 어떻게 구현되는지 살펴보겠습니다!의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!