Vue組件開發實踐之scopedSlot的傳遞

收錄待用,修改轉載已取得騰訊雲受權html


導語

現今的前端開發都講究模塊化組件化,即把公共的交互和功能封裝到一個個的組件之中,在開發總體界面的時候就能像搭積木同樣快速清晰高效。在使用Vue開發咱們的vhtml-ui的組件庫的過程當中遇到了組件嵌套組件時須要傳遞scopedSlot的狀況,官方的文檔和教程目前尚未比較明確的指引,因此摸着石頭過河,調通了想要的效果。記錄下來方便你們和本身。前端

在Vue中,爲了讓組件能夠組合,咱們使用Slot來混合父組件的內容與子組件本身的模板。這樣就實現了Vue的內容分發。vue

Scoped Slot(做用域插槽)是在Vue 2.1引入的更進階的功能,它是一種特殊類型的slot,用做使用一個(可以傳遞數據到)可重用模板替換已渲染元素。個人理解就是使用scoped slot能在插槽裏自定義模板而且使用組件傳遞過來的context。這大大提升了組件開發的靈活性。node

Select組件一期

在開發咱們的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>&nbsp;|&nbsp;<span>{{ props.item.value }}<span></div><div>{{ props.item.area }}</div>
                <div>{{ props.item.url }}</div>
            </div>
        </template>
    </v-select>

很好,很是好,如今有一個新需求:這個列表有的時候想要脫離select使用,好比就直接展現在頁面上,不須要經過下拉彈出。編程

select-list組件

這好辦啊,做爲組件開發的老司機們天然能想到把這個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的介紹和例子。

Render函數和JSX

人總不能讓尿給憋死,一條路走不通咱們就看看有沒有其餘辦法。在Vue的官方文檔上有這麼一句話:

「 Vue 推薦在絕大多數狀況下使用 template 來建立你的 HTML。然而在一些場景中,你真的須要 JavaScript 的徹底編程的能力,這就是 render 函數,它比 template 更接近編譯器。」

查看文檔,經過render函數確實可以傳遞scoped slot,如下圖的方式

把scoped slot做爲createElement方法的第二參數(data object)的一個屬性傳遞到子組件中。

可是render函數的缺點就是不靈活,特別是在定義你的組件的dom結構模板的時候,若是寫不少 render 函數,可能會以爲痛苦。它比較適用於外層組件僅僅是對內層組件的一次邏輯封裝,而渲染的模板結構變化擴展很少的狀況。

還好咱們還有最後一把殺手鐗--JSX。它可讓咱們回到於更接近模板的語法上。具體關於JSX的使用不是本文的主題,咱們能夠閱讀使用文檔 ,學習關於 JSX 映射到 JavaScript的用法。

JSX實現上文的嵌套例子

經過參閱文檔及不斷地摸索,最終實現了本身想要的功能。咱們直接上關鍵代碼(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)

JSX中對template經常使用點的轉換

上面的介紹涵蓋了基本的用法,可是咱們在組件中每每會用一些不基本但經常使用的vue特性。咱們接下來一塊兒看看。

細心地小夥伴可能發現了上面的代碼中已經出現了這些用法

directives

若是咱們在組件中使用了directives,那麼jsx裏就不能想以前在template裏那麼天然的書寫

v-popper:third.click

而是須要workaround:

  1. Pass everything as an object via value, e.g. v-name={{ value, modifier: true }}

  2. Use the raw vnode directive data format:

上面的例子中就是用了方法二。

v-model

render函數(JSX也是寫在render函數中)中沒有與v-model相應的api - 你必須本身來實現相應的邏輯。即經過value屬性傳遞值,並經過綁定input事件來響應變化。

沒有template 中的v-ifv-for:

這意味着咱們須要在render函數或者JSX的表達式中手寫if-else邏輯判斷。或者如本例中使用三目表達式來實現。

這就是深刻底層要付出的,儘管麻煩了一些,但你能夠更靈活地控制。

但願這邊文章能讓咱們在開發Vue組件的時候少走一些彎路,若是有大神有更好的辦法或直接在template中實現傳遞scoped slot的功能,請多多指教!


原文連接:https://www.qcloud.com/community/article/693017

相關文章
相關標籤/搜索