這篇文章跟大家分享一下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
用法先跟大家一起回顧下插槽的大概用法。這裡的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 的!事不宜遲,趕快
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
两种具名插槽写法的区别。
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
中
##header插槽
中的render函數,是在子元件render 的時候執行
#預設插槽
正是
'default'。可以發現,
這裡並沒有像上面 header插槽 一樣,去執行 render,而是直接將我們之前得到的 插槽vNode回來了。
作用域插槽
的
data 數據,這就是為什麼我們在
App元件 能透過
作用域插槽 存取到子元件資料的原因了
預設插槽,不管v2.5 、
v2.6 的寫法,都是在父元件中生成
vNode。
vNode 存在
vm.$slot 中。待子元件
render 到插槽時,
會直接拿到父元件的vNode
兩個版本情況不一。根據編譯結果可知:
的寫法,跟預設插槽是一樣的,在父元件產生vNode,子元件直接拿來用
中,直接時在子元件中才去執行插槽render
,產生插槽vNode
。
。不管版本,都是在子元件中進行render的。
中的插槽,都會在子元件執行render 的時候才會去產生vNode。
。如果是v2.5 ,具名插槽和預設插槽都只在父元件render 的時候產生vNode,子元件要渲染slot 的時候,直接在父元件實例的$slot 中取得已經是vNode的資料。
普通插槽。如果是 v2.6
,則具名插槽 雖然是在子元件中執行的 render,但其不接收參數。
作用域插槽。 不管 v2.5
或 v2.6
,都只在 子元件執行 render,並且能接收參數。
好了,最後來個精煉的總結。 作用域插槽一定是延遲執行,且接收參數!普通插槽 可能延遲執行,可能直接執行,但不接收參數!
寫在最後,很多時候我們搬磚,遵照文檔把功能實現確實省力省心~但當你做多了,你就發現當前的東西缺乏挑戰,索然無味。那這個時候,就會有一種衝動,想深入實現原理,看看 slot
到底是怎麼實現的。特別是作用域插槽。用的時候都會想當然的覺得在上層組件透過作用域插槽拿到子組件的數據理所當然應,但是在深入源碼之後,看懂了別人是怎麼做的,會有突然恍然大悟的感覺~
以上是聊聊Vue.slot原理,起探究下slot 是怎麼實現的!的詳細內容。更多資訊請關注PHP中文網其他相關文章!