收錄待用,修改轉載已取得騰訊雲受權html
現今的前端開發都講究模塊化組件化,即把公共的交互和功能封裝到一個個的組件之中,在開發總體界面的時候就能像搭積木同樣快速清晰高效。在使用Vue開發咱們的vhtml-ui的組件庫的過程當中遇到了組件嵌套組件時須要傳遞scopedSlot的狀況,官方的文檔和教程目前尚未比較明確的指引,因此摸着石頭過河,調通了想要的效果。記錄下來方便你們和本身。前端
在Vue中,爲了讓組件能夠組合,咱們使用Slot來混合父組件的內容與子組件本身的模板。這樣就實現了Vue的內容分發。vue
Scoped Slot(做用域插槽)是在Vue 2.1引入的更進階的功能,它是一種特殊類型的slot,用做使用一個(可以傳遞數據到)可重用模板替換已渲染元素。個人理解就是使用scoped slot能在插槽裏自定義模板而且使用組件傳遞過來的context。這大大提升了組件開發的靈活性。node
在開發咱們的select組件時很天然就用上了scoped slot這一特性。咱們須要遍歷數據中的選項數組,渲染成界面上的下拉選項列表。若是是比較複雜的容許自定義的list item,在組件裏寫死dom結構就行不通了,好比:git
有了scoped slot實現很輕鬆:github
<v-select kind="popup" :options="options4"> <template slot="listItem" scope="props"> <div> <div><span>{{ props.item.text }}<span> | <span>{{ props.item.value }}<span></div><div>{{ props.item.area }}</div> <div>{{ props.item.url }}</div> </div> </template> </v-select>
很好,很是好,如今有一個新需求:這個列表有的時候想要脫離select使用,好比就直接展現在頁面上,不須要經過下拉彈出。編程
這好辦啊,做爲組件開發的老司機們天然能想到把這個list獨立作成一個組件,頁面能夠直接調用,select組件也能夠在它之上再封裝一層。api
完美!數組
開幹!babel
select-list template結構示意:
<ul class="v-select-list"> <li class="v-select-list__item" v-for="(item, index) in options"> <slot name="listItem" :item="item"> <span>{{ item.text }}</span> </slot> </li> </ul>
select template結構示意:
<div class="v-select"> <v-popper> <v-select-list> <slot name="listItem"> </slot> </v-select-list> </v-popper> </div>
而後問題來了,最裏層的select-list組件並無接收到用戶自定義的scoped slot。經過查找Vue官方文檔以及谷歌,也沒有找到使用template方式傳遞scoped slot的介紹和例子。
人總不能讓尿給憋死,一條路走不通咱們就看看有沒有其餘辦法。在Vue的官方文檔上有這麼一句話:
「 Vue 推薦在絕大多數狀況下使用 template 來建立你的 HTML。然而在一些場景中,你真的須要 JavaScript 的徹底編程的能力,這就是 render 函數,它比 template 更接近編譯器。」
查看文檔,經過render函數確實可以傳遞scoped slot,如下圖的方式
把scoped slot做爲createElement方法的第二參數(data object)的一個屬性傳遞到子組件中。
可是render函數的缺點就是不靈活,特別是在定義你的組件的dom結構模板的時候,若是寫不少 render 函數,可能會以爲痛苦。它比較適用於外層組件僅僅是對內層組件的一次邏輯封裝,而渲染的模板結構變化擴展很少的狀況。
還好咱們還有最後一把殺手鐗--JSX。它可讓咱們回到於更接近模板的語法上。具體關於JSX的使用不是本文的主題,咱們能夠閱讀使用文檔 ,學習關於 JSX 映射到 JavaScript的用法。
經過參閱文檔及不斷地摸索,最終實現了本身想要的功能。咱們直接上關鍵代碼(select的render函數),看看有什麼奧祕:
render(h) { let directives = [{ name: 'popper', arg: 'selector', modifiers: { click: true } }]; return ( <div class={{ 'v-select': true, 'is-open': this.isPopperShown, 'is-disabled': this.disabled }}> <v-popper ref="selector" placement="bottom-start" onVisibleChange={this.togglePopper}> <v-select-list class="v-select__options" onChange={this.handleChange} multiple={this.multiple} value={this.currentValue} onInput={this.handleListInput} scopedSlots={{listItem: this.$scopedSlots.listItem}} > </v-select-list> </v-popper> <div class="v-select__header" { ...{directives} }> { this.currentIndex !== -1 && this.$scopedSlots.headItem ? this.$scopedSlots.headItem(this.current) : (<span>{this.currentText}</span>) } <v-icon class="v-select__header-arrow" name="v-arrow_dropdown"> </v-icon> </div> </div> ) }
在子組件的標籤上經過scopedSlots
屬性能夠向其傳遞本身的scoped slot;
自身的scoped slot能夠經過this.$scopedSlots
對象獲取,默認就是default,具名slot就是它的名字。本例爲「listItem」;
若是不在標籤上傳遞而是須要使用表達式傳遞,也能夠經過 this.$scopedSlots
對象。而且一個具體的scoped slot對象其實就是一個函數,其內部的scope能夠在參數中傳入。好比本例中的this.$scopedSlots.headItem(this.current)
上面的介紹涵蓋了基本的用法,可是咱們在組件中每每會用一些不基本但經常使用的vue特性。咱們接下來一塊兒看看。
細心地小夥伴可能發現了上面的代碼中已經出現了這些用法
若是咱們在組件中使用了directives,那麼jsx裏就不能想以前在template裏那麼天然的書寫
v-popper:third.click
而是須要workaround:
Pass everything as an object via value, e.g. v-name={{ value, modifier: true }}
Use the raw vnode directive data format:
上面的例子中就是用了方法二。
render函數(JSX也是寫在render函數中)中沒有與v-model
相應的api -
你必須本身來實現相應的邏輯。即經過value屬性傳遞值,並經過綁定input事件來響應變化。
v-if
和 v-for
:這意味着咱們須要在render函數或者JSX的表達式中手寫if-else邏輯判斷。或者如本例中使用三目表達式來實現。
這就是深刻底層要付出的,儘管麻煩了一些,但你能夠更靈活地控制。
但願這邊文章能讓咱們在開發Vue組件的時候少走一些彎路,若是有大神有更好的辦法或直接在template中實現傳遞scoped slot的功能,請多多指教!