您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“Vue.slot原理及slot是如何實現(xiàn)的”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!
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>
如上代碼塊可以發(fā)現(xiàn):
子組件中的 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插槽 的作用域數(shù)據(jù)' } } } } </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>
如上代碼塊可以發(fā)現(xiàn):
子組件中的 slot標簽
除了有 name=footer
的屬性,還有一個:footerInfo="footerInfo"
的屬性(作用就是傳遞子組件數(shù)據(jù))
父組件中的 template標簽
不僅有 v-slot:footer
,并且還有一個賦值操作 ="slotProps"
,在模版的雙括號語法中,直接通過 slotProps
訪問到 子組件的 footerInfo
頁面展示效果如圖:
好了,簡單回顧完用法后,筆者在這里先提三個問題:
普通插槽、 作用域插槽 的 vNode 是在哪個環(huán)節(jié)生成的,render 父組件時還是子組件時?
作用域插槽 為什么能在父組件訪問到子組件的數(shù)據(jù)?
普通插槽 跟 作用域插槽 在實現(xiàn)上有區(qū)別嗎?
我們帶著疑問接著往下看!
slot
的編譯區(qū)別我們根據(jù)上述最終的案例代碼,執(zhí)行一下打包命令,看看 Vue 在編譯模板的時候,是怎么處理我們的 slot
的!事不宜遲,趕緊 build
一哈~(偷偷告訴大?,Vue
處理 作用域插槽 跟 普通插槽 的差異就是從編譯開始的,也就是 render函數(shù) 會有所不同)
這里筆者順便使用 v2.5
的具名插槽寫法給大?參照一下(對具名插槽header做改寫,使用 slot="header"
的寫法),大家可以看下 v2.6
、v2.5
具名插槽的 寫法、實現(xiàn) 上的區(qū)別~反正也不難,也就順便帶出來看看了
上圖左邊是 v2.6
、右邊是 v2.5
的,這里,我們集中關(guān)注:
scopedSlots
屬性。使用作用域插槽的 footer
的 render函數(shù) 是放在 scopedSlots
里的,并且 函數(shù)中 還有接收一個參數(shù)
my-slot
的 children
??梢园l(fā)現(xiàn),默認插槽的 render函數(shù)
一直都是作為當前組件的childre節(jié)點
,放在 當前 組件render函數(shù) 的第三個參數(shù)中
關(guān)注 header
兩種具名插槽寫法的區(qū)別。
v2.6 中,我們使用了具名插槽,但是又未使用 作用域插槽的 header 也被放在了 scopedSlots
,但是函數(shù)的參數(shù)為空,這點跟作用域插槽有區(qū)別。
v2.5 中, 具名插槽header 僅僅作為 my-slot組件
的children節(jié)點,并且其render函數(shù)的第二個參數(shù)中有一個 slot
的屬性。
其實根據(jù)上述編譯后的結(jié)果,我們不妨這樣猜測:
默認插槽直接在父組件的 render
階段生成 vNode
。
子組件 render
時,可能通過某種手段取得已經(jīng)生成好的 插槽vNode
用作自己的 slot
節(jié)點。
因為觀察上述默認插槽的render函數(shù):e("h2", [t._v("默認插槽")])
,直接就是 my-slot
組件的childre節(jié)點(位于 my-slot
組件的第三個參數(shù))。
作用域插槽是在子組件 render
階段生成 vNode
。
因為我們觀察作用域插槽 footer
的編譯后結(jié)果,其不作為 my-slot
組件的 children,而是被放在了 my-slot
組件的 第二個參數(shù) data
中的一個 scopedSlots屬性
里。
并且,作用域插槽的 render 函數(shù) 外層的 funciton 接收了一個參數(shù)。如果在執(zhí)行子組件 render 的時候調(diào)用,那完全可以拿到子組件的數(shù)據(jù)。
這里放出具體的 作用域插槽 打包后代碼,大家一看就很清晰了:
{ scopedSlots: t._u([ { key: "footer", // 函數(shù)接收了一個參數(shù)n fn: function (n) { return [ // h2 標簽的 render 函數(shù) e("h2", [t._v("footer 具名 + 作用域插槽")]), // p 標簽的 render 函數(shù),這里可以看到編譯后是:n.footerInfo.text e("p", [t._v(t._s(n.footerInfo.text))]) ] } } ]) }
slot
實現(xiàn)原理為了方便大家看調(diào)試結(jié)果,當前項目的組件結(jié)構(gòu)主要是這樣,有三大層:
Vue
-><App />
-><my-slot />
這里筆者在運行時代碼 initRender()
、renderSlot()
中,打上 debugger ,直接帶大火看看執(zhí)行流程。這里簡單介紹下兩個方法:
initRender:獲取 vm.$slot
。組件初始化時執(zhí)行(比如執(zhí)行到 my-slot組件
時,可從vm.$slot 獲取父組件中的slot vNode
,也就是我們的 默認插槽)
renderSlot:把組件的 slot
替換成真正的 插槽vNode
接下來直接看實驗截圖:
1、先是進入initRender()
(這里跳過初始化 大Vue
、App
的過程)。直接到初始化 my-slot組件
過程?!?簡單解釋:由于 App組件
執(zhí)行 render
得到 App組件vNode ,在 patch
過程中 遇到 vue-component-my-slot
的 vNode ,又執(zhí)行 my-slot組件
的 初始化流程?!?/p>
我們不難發(fā)現(xiàn),圖中此時正值 my-slot組件
的 init
階段。
再往下執(zhí)行,我們可以得到 App組件中的 <h2>默認插槽</h2>
的vNode,賦值給 vm.$slot
(這里我們記住,默認插槽的 vNode 已經(jīng)得到)
2、再是進入 renderSlot()
。接著上面繼續(xù)單步執(zhí)行,會走到 renderSlot
中。這時候,已經(jīng)進入到 my-slot組件
的 render
階段了?;仡櫟谝徊街校藭r我們手握 默認插槽的vNode,并存在 vm.$slot.default
中
header插槽
按順序走,先是 render 排第一的 header 的 vNode。如圖所示,會走到斷點處,我們接著單步
直接進入執(zhí)行我們 header插槽 的render函數(shù)執(zhí)行處。根據(jù)調(diào)試步驟,我們可以肯定,放置在 scopedSlots屬性
中的 render函數(shù),是在子組件 render 的時候執(zhí)行
得到 header插槽 的 vNode
默認插槽
繼續(xù)單步走,這次輪到 默認插槽 了!如圖所示,這里的 key
正是 'default'
??梢园l(fā)現(xiàn),這里并沒有像上面 header插槽 一樣,去執(zhí)行 render,而是直接將我們之前得到的 插槽vNode返回了。
得到 default插槽 的 vNode
作用域插槽
前面都跟 header插槽 一致,都是會在 my-slot 組件中 執(zhí)行插槽的 render。我們直接單步到 render 處看看有什么區(qū)別。這里可以得出,function處傳入的參數(shù)正是我們子組件 my-slot
的 data
數(shù)據(jù),這就是為什么我們在 App組件
能通過 作用域插槽
訪問到子組件數(shù)據(jù)的原因了
最后也是返回 footer插槽 的vNode。好了,驗證過程結(jié)束~
其實上面的流程只是論證過程,大家不可以不必深陷其中。筆者在這里直接根據(jù)實踐過程,給大伙總結(jié)出結(jié)論!也就是要回到我們一開始的三個問題!
1、普通插槽、 作用域插槽 的 vNode 是在哪個環(huán)節(jié)生成的,render 父組件時還是子組件時?
默認插槽,不管 v2.5
、 v2.6
的寫法,都是在 父組件中生成 vNode
。vNode
存在 vm.$slot
中。待子組件 render
到插槽時,會直接拿到 父組件的 vNode
具名插槽兩個版本情況不一。根據(jù)編譯結(jié)果可知:
v2.5
的寫法,跟默認插槽是一樣的,在父組件生成vNode,子組件直接拿來用
v2.6
中,直接時在 子組件 中才去執(zhí)行 插槽render
,生成 插槽vNode
。
作用域插槽。不管版本,都是在子組件中進行render的。
大家不妨這么理解,模版編譯后,只要是被放在 scopeSlots屬性 中的插槽,都會在子組件執(zhí)行 render 的時候才會去生成vNode。
2、作用域插槽 為什么能在父組件訪問到子組件的數(shù)據(jù)?
作用域插槽只有子組件render的時候,才會執(zhí)行render生成vNode。并且,作用域插槽的 render 函數(shù)能接參數(shù),從而獲得子組件的數(shù)據(jù)。就是這樣形成了作用域插槽!所以我們能在父組件中,訪問到子組件的data數(shù)據(jù)。
3、普通插槽 跟 作用域插槽 在實現(xiàn)上有區(qū)別嗎?
有區(qū)別。
普通插槽。如果是 v2.5
,具名插槽 和 默認插槽 都只在 父組件 render 的時候生成 vNode,子組件要 渲染slot 的時候,直接在父組件實例的 $slot 中獲取已經(jīng)是vNode的數(shù)據(jù)。
普通插槽。如果是 v2.6
,具名插槽 雖然是在子組件中執(zhí)行的 render,但是其不接收參數(shù)。
作用域插槽。不管 v2.5
還是 v2.6
,都只在 子組件執(zhí)行 render,并且能接收參數(shù)。
“Vue.slot原理及slot是如何實現(xiàn)的”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。