Vue.js之slot深度復(fù)制詳解
前言
在Vue中,slot是一個很有用的特性,可以用來向組件內(nèi)部插入一些內(nèi)容。slot就是“插槽”的意思,用大白話說就是:定義組件的時候留幾個口子,由用戶來決定插入的內(nèi)容。
例如我們定義一個組件MyComponent,其包含一個slot:
Vue.component('MyComponent', { template: ` <div> <slot></slot> </div> ` })
當(dāng)調(diào)用<MyComponent>123</MyComponent>
時,會渲染為如下DOM結(jié)構(gòu):
<div> 123 </div>
現(xiàn)在又有新需求了,我們希望調(diào)用<MyComponent>123</MyComponent>
時,渲染出這樣的DOM結(jié)構(gòu):
<div> 123 123 </div>
看起來很容易實(shí)現(xiàn),即再為MyComponent添加一個slot:
Vue.component('MyComponent', { template: ` <div> <slot></slot> <slot></slot> </div> ` })
渲染出的結(jié)構(gòu)也確實(shí)如你所愿,唯一美中不足的是控制臺有一個小小的Warning:
Duplicate presence of slot "default" found in the same render tree
如果你不是強(qiáng)迫癥患者,這時候你可以收工安心回家睡覺了。直到有一天你的同事向你抱怨,為什么向MyComponent插入一個自定義組件會渲染不出來?
例如有一自定義組件MyComponent2:
Vue.component('MyComponent2', { template: ` <div>456</div> ` })
當(dāng)調(diào)用<MyComponent><MyComponent2></MyComponent2></MyComponent>
時,預(yù)期渲染為如下DOM結(jié)構(gòu):
<div> <div>456</div> <div>456</div> </div>
為什么不能正常工作呢?估計是前面的那個Warning搞得鬼,通過查詢發(fā)現(xiàn)在Vue 2.0中不允許有重名的slot:
重名的 Slots 移除
同一模板中的重名 已經(jīng)棄用。當(dāng)一個 slot 已經(jīng)被渲染過了,那么就不能在同一模板其它地方被再次渲染了。如果要在不同位置渲染同一內(nèi)容,可一用 prop 來傳遞。
文檔中提示可以用props來實(shí)現(xiàn),然而在我的用例中顯然是不合適的。經(jīng)過搜索后,最靠譜的方法是手寫render函數(shù),將slot中的內(nèi)容復(fù)制到其他的位置。
將之前的MyComponent改為render函數(shù)的方式定義:
Vue.component('MyComponent', { render (createElement) { return createElement('div', [ ...this.$slots.default, ...this.$slots.default ]) } })
在上面的定義中我們插入了兩個this.$slots.default,測試下能不能正常工作。然而并沒有什么卵用,Vue文檔在render函數(shù)這一章有以下說明:
VNodes 必須唯一
所有組件樹中的 VNodes 必須唯一
這意味著我們不能簡單地在不同位置引用this.$slots.default,必須對slot進(jìn)行深度復(fù)制。深度復(fù)制的函數(shù)如下:
function deepClone(vnodes, createElement) { function cloneVNode (vnode) { const clonedChildren = vnode.children && vnode.children.map(vnode => cloneVNode(vnode)); const cloned = createElement(vnode.tag, vnode.data, clonedChildren); cloned.text = vnode.text; cloned.isComment = vnode.isComment; cloned.componentOptions = vnode.componentOptions; cloned.elm = vnode.elm; cloned.context = vnode.context; cloned.ns = vnode.ns; cloned.isStatic = vnode.isStatic; cloned.key = vnode.key; return cloned; } const clonedVNodes = vnodes.map(vnode => cloneVNode(vnode)) return clonedVNodes; }
上面的核心函數(shù)就是cloneVNode()
,它遞歸地創(chuàng)建VNode,實(shí)現(xiàn)深度復(fù)制。VNode的屬性很多,我并不了解哪些是關(guān)鍵屬性,只是參照著Vue的源碼一并地復(fù)制過來。
基于以上函數(shù),我們更改MyComponent的定義:
Vue.component('MyComponent', { render (createElement) { return createElement('div', [ ...this.$slots.default, ...deepClone(this.$slots.default, createElement) ]) } })
經(jīng)測試,一切正常。
總結(jié)
在Vue 1.0中重名的slots并不會出現(xiàn)什么問題,不知道為什么在2.0中取消了這個功能。我聽說React提供了復(fù)制Element的標(biāo)準(zhǔn)函數(shù),希望Vue也能提供這個函數(shù),免得大家踩坑。以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。
相關(guān)文章
Vue數(shù)據(jù)更新但頁面沒有更新的多種情況問題及解決
這篇文章主要介紹了Vue數(shù)據(jù)更新但頁面沒有更新的多種情況問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07vue項目使用高德地圖時報錯:AMap?is?not?defined解決辦法
這篇文章主要給大家介紹了關(guān)于vue項目使用高德地圖時報錯:AMap?is?not?defined的解決辦法,"AMap is not defined"是一個錯誤提示,意思是在代碼中沒有找到定義的AMap,需要的朋友可以參考下2023-12-12解決Vue axios post請求,后臺獲取不到數(shù)據(jù)的問題方法
今天小編就為大家分享一篇解決Vue axios post請求,后臺獲取不到數(shù)據(jù)的問題方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08Vue項目打包壓縮的實(shí)現(xiàn)(讓頁面更快響應(yīng))
這篇文章主要介紹了Vue項目打包壓縮的實(shí)現(xiàn)(讓頁面更快響應(yīng)),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03Ant?Design?Vue?修改表格頭部樣式的詳細(xì)代碼
這篇文章主要介紹了Ant?Design?Vue?修改表格頭部樣式,首先用到的是customHeaderRow這個API,類型是一個函數(shù),本文通過完整代碼給大家詳細(xì)講解,需要的朋友可以參考下2022-10-10