本文主要描述不才在學習Vue和Bootstrap中遇到的關於插槽的問題及解決方案
vue官網和不少熱心朋友又不少關於默認插槽,具名插槽,插槽做用域相關的解釋,我就不重複搬過來佔用篇幅了,這裏簡單的談談我我的的淺見。html
爲何要定義組件?
答:定義組件是一種封裝的形式,使用最簡單的標籤及屬性配置表達一大段比較豐富的結構效果及一些數據和事件。vue
<div id="accordion"> <div class="card"> <div class="card-header" id="headingOne"> <h5 class="mb-0"> <button class="btn btn-link" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne"> Collapsible Group Item #1 </button> </h5> </div> <div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordion"> <div class="card-body"> Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. </div> </div> </div> <div class="card"> <div class="card-header" id="headingTwo"> <h5 class="mb-0"> <button class="btn btn-link collapsed" data-toggle="collapse" data-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"> Collapsible Group Item #2 </button> </h5> </div> <div id="collapseTwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordion"> <div class="card-body"> Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. </div> </div> </div> <div class="card"> <div class="card-header" id="headingThree"> <h5 class="mb-0"> <button class="btn btn-link collapsed" data-toggle="collapse" data-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree"> Collapsible Group Item #3 </button> </h5> </div> <div id="collapseThree" class="collapse" aria-labelledby="headingThree" data-parent="#accordion"> <div class="card-body"> Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS. </div> </div> </div> </div>
<widget-collapse> <div header="Collapsible Group Item #1"> Anim pariatur ... </div> <div header="Collapsible Group Item #2"> Anim pariatur ... </div> <div header="Collapsible Group Item #3"> Anim pariatur ... </div> </widget-collapse>
<slot></slot>
恰好能夠解決內容中的標籤解析問題,但它會將全部的內容(上例中3個div)都解析到一塊兒沒法拆分<slot name="..."></slot>
能夠解決內容沒法拆分的問題。那咱們就把內容中的每個都標一個name, name取值要知足內容個數不定的條件,因此最好採用和序號有關的,恰好能夠在v-for指令中匹配到。Vue.component('widget-collapse', { template: `<div> <div class="card" v-for="(item, index) in vitems"> <div class="card-header"> <h5 class="mb-0"> <button class="btn btn-link" data-toggle="collapse">{{item.header}}</button> </h5> </div> <div :class="['collapse']"> <div class="card-body"> <slot :name="index"></slot> </div> </div> </div> </div>`, data() { let children = this.$slots, items = []; for (let i in children) { let node = children[i][0]; if (node.tag) { items[i] = ({ header: VTool.attr(node, 'header') || ('Item ' + items.length), }) } } return { vitems: items } } })
<widget-collapse> <div header="Collapsible Group Item #1" slot="0"> Anim pariatur ... </div> <div header="Collapsible Group Item #2" slot="1"> Anim pariatur ... </div> <div header="Collapsible Group Item #3" slot="2"> Anim pariatur ... </div> </widget-collapse>
使用時每一個節點都寫一個slot太累贅,容易寫錯,刪除某個節點或調整節點順序後更改麻煩,可否程序動態幫忙添加該slot屬性?
答:未找到對應API,但對比默認與具名插槽的$slots數據對象發現key分別爲default和具名namenode
Vue.component('widget-collapse', { template: `<div> <div class="card" v-for="(item, index) in vitems"> <div class="card-header"> <h5 class="mb-0"> <button class="btn btn-link" data-toggle="collapse">{{item.header}}</button> </h5> </div> <div :class="['collapse']"> <div class="card-body"> <slot :name="item.id"></slot> </div> </div> </div> </div>`, data() { let children = this.$slots.default, items = []; for (let i = 0, len = children.length; i < len; i++) { let node = children[i]; if (node.tag) { let id = VTool.random(); this.$slots[id] = node; items.push({ id: id, header: VTool.attr(node, 'header') || ('Item ' + items.length), }) } } return { vitems: items } } })
<widget-collapse> <div header="Collapsible Group Item #1"> Anim pariatur ... </div> <div header="Collapsible Group Item #2"> Anim pariatur ... </div> <div header="Collapsible Group Item #3"> Anim pariatur ... </div> </widget-collapse>
updateChildComponent
方法內在更新下層組件時執行了vm.$slots = resolveSlots(renderChildren, parentVnode.context);
覆蓋了以前縮處理過的$slots,且會在vm._render
方法中從新渲染,且在此以前沒有公佈任何事件,那就暴力覆蓋重寫了)Vue.component('widget-collapse', { ... created() { let _render = this._render; this._render = function() { let $slots = this.$slots; for (let name in $slots) { if (name !== 'default') { return _render.apply(this, arguments); } } // 此時確定是從新new的$slots, 重寫對應關係 let children = this.$slots.default, items = []; for (let i = 0, len = children.length; i < len; i++) { let node = children[i]; if (node.tag) { let id = VTool.random(); this.$slots[id] = node; items.push({ id: id, header: VTool.attr(node, 'header') || ('Item ' + items.length), }) } } this.vitems = items; return _render.apply(this, arguments); } }, ... })
本文對於vue插槽的處理辦法略顯暴力,但的確解決了我遇到的問題,各位大拿有更好的經驗還請不吝賜教,拜謝!