Vue.js之slot深度複製詳解,vue.jsslot深度

來源:互聯網
上載者:User

Vue.js之slot深度複製詳解,vue.jsslot深度

前言

在Vue中,slot是一個很有用的特性,可以用來向組件內部插入一些內容。slot就是“插槽”的意思,用大白話說就是:定義組件的時候留幾個口子,由使用者來決定插入的內容。

例如我們定義一個組件MyComponent,其包含一個slot:

Vue.component('MyComponent', { template: ` <div>  <slot></slot> </div> `})

當調用<MyComponent>123</MyComponent>時,會渲染為如下DOM結構:

<div> 123</div>

現在又有新需求了,我們希望調用<MyComponent>123</MyComponent>時,渲染出這樣的DOM結構:

<div> 123 123</div>

看起來很容易實現,即再為MyComponent添加一個slot:

Vue.component('MyComponent', { template: ` <div>  <slot></slot>  <slot></slot> </div> `})

渲染出的結構也確實如你所願,唯一美中不足的是控制台有一個小小的Warning:

Duplicate presence of slot "default" found in the same render tree

如果你不是強迫症患者,這時候你可以收工安心回家睡覺了。直到有一天你的同事向你抱怨,為什麼向MyComponent插入一個自訂群組件會渲染不出來?

例如有一自訂群組件MyComponent2:

Vue.component('MyComponent2', { template: ` <div>456</div> `})

當調用<MyComponent><MyComponent2></MyComponent2></MyComponent>時,預期渲染為如下DOM結構:

<div> <div>456</div> <div>456</div></div>

為什麼不能正常工作呢?估計是前面的那個Warning搞得鬼,通過查詢發現在Vue 2.0中不允許有重名的slot:

重名的 Slots 移除

同一模板中的重名 已經棄用。當一個 slot 已經被渲染過了,那麼就不能在同一模板其它地方被再次渲染了。如果要在不同位置渲染同一內容,可一用 prop 來傳遞。

文檔中提示可以用props來實現,然而在我的用例中顯然是不合適的。經過搜尋後,最靠譜的方法是手寫render函數,將slot中的內容複寫到其他的位置。

將之前的MyComponent改為render函數的方式定義:

Vue.component('MyComponent', { render (createElement) { return createElement('div', [  ...this.$slots.default,  ...this.$slots.default ]) }})

在上面的定義中我們插入了兩個this.$slots.default,測試下能不能正常工作。然而並沒有什麼卵用,Vue文檔在render函數這一章有以下說明:

VNodes 必須唯一

所有組件樹中的 VNodes 必須唯一

這意味著我們不能簡單地在不同位置引用this.$slots.default,必須對slot進行深度複製。深度複製的函數如下:

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;}

上面的核心函數就是cloneVNode() ,它遞迴地建立VNode,實現深度複製。VNode的屬性很多,我並不瞭解哪些是關鍵屬性,只是參照著Vue的源碼一併地複製過來。

基於以上函數,我們更改MyComponent的定義:

Vue.component('MyComponent', { render (createElement) { return createElement('div', [  ...this.$slots.default,  ...deepClone(this.$slots.default, createElement) ]) }})

經測試,一切正常。

總結

在Vue 1.0中重名的slots並不會出現什麼問題,不知道為什麼在2.0中取消了這個功能。我聽說React提供了複製Element的標準函數,希望Vue也能提供這個函數,免得大家踩坑。以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的協助,如果有疑問大家可以留言交流。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.