1. 程式人生 > 程式設計 >vue下拉列表的兩種實現方式比較

vue下拉列表的兩種實現方式比較

vue下拉列表的兩種實現

第一種採用v-for的方式

  <el-select v-model="form.columeType" placeholder="欄位型別">
           <el-option v-for="(item,index) in columeTyhttp://www.cppcns.compeArr" :key="index" :label="item.label" :value="item.value">
           </el-option>
  </el-select>

這種方式需要在data中定義columeTypeArr,如下

data(){
    return {
       columeTypeArr:[{
            value:'String',label:'字串'
        },{
            value:'Int',label:'整數',},{
            value:'Decimal',label:'數值型'
        }],}
}

第二種採用寫死的方式

直接在select下寫死

  <el-select v-model="form.fileOrgType" placeholder="請選擇">
        <el-option label="是" value="Y"> </el-option>
        <el-option label="否" value="N"></el-option>
  </el-select>

兩種方式的比較:

兩種方式都差不多,只是第一種方式需要在data中進行配置,對於需要資料從後臺回顯的情況,明顯是第一種方法好。

對於簡單的下拉列表引數很少的情況,第二種明顯更佔優。

vue下拉選單元件的實現

我們一起來實現一個vue的下拉選單元件。

像這種基本UI元件,網上已經有很多了,為什麼要自己實現呢?其實並不是有意重複造輪子,而是想通過這個過程回顧一下vue元件開發的一些細節和注意事項。

為什麼選擇下拉選單元件?

因為:麻雀雖小五臟俱全,這個小小的元件涉及到了不少vue元件開發的知識點。

好了,那就開始吧!

首先建立一個vue-cli的專案,筆者用的是vue-cli3,建立過程略,然後建立一個vue元件:DropDownList.vue

在編寫模板之前,我們來分析一下這個元件的檢視結構和功能。

下拉選單元件應該由兩部分組成:

選中項的文字

待選選單(預設隱藏)

它的主要功能包括:

滑鼠經過下拉選單元件,顯示待選選單

滑鼠滑出下拉選單元件,隱藏待選選單

滑鼠點選待選選單中的條目,選中項文字更新,元件派發change事件

我們編寫如下這樣的模板:

<template>
  <div class="zq-drop-list" @mouseover="onDplOver($event)" @mouseout="onDplOut($event)">
        <span>選中項的文字<i></i></span>
        <ul>
            <li>北京</li>
            <li>上海</li>
            <li>廣州</li>
        </ul>
    </div>
</template>

選中項文字右側的i標籤,用來實現下拉選單的三角形圖示,在下文的css中我們用背景圖來實現。

我們給根元素div已經添加了滑鼠經過和滑出的回撥函式,具體實現見下文。

接下來我們為這個下拉選單編寫樣式,在模板下方新增style標籤,為了防止和其他元件的樣式發生衝突,筆者建議大家在開發元件時,都給style加上scoped屬性。另外,筆者在這裡用到了scss,具體程式碼如下:

<style scoped lang="scss">
    .zq-drop-list{
        display: inline-block;
        min-width: 100px;
        position: relative;
        span{
            display: block;
            height: 30px;
            line-height: 30px;
            background: #f1f1f1;
            font-size: 14px;
            text-align: center;
            color: #333333;
            border-radius: 4px;
            i{
                background: url(https://www.easyicon.net/api/resizeApi.php?id=1189852&size=16) no-repeat center center;
                margin-left: 6px;
                display: inline-block;
            }
        }
        ul{
            position: absolute;
            top: 30px;
            left: 0;
            width: 100%;
            margin: 0;
            padding: 0;
            border: solid 1px #f1f1f1;
            border-radius: 4px;
            overflow: hidden;
            li{
                list-style: none;
                height: 30px;
                line-height: 30px;
                font-size: 14px;
                border-bottom: solid 1px #f1f1f1;
                background: #ffffff;
            }
            li:last-child{
                border-bottom: none;
            }
            li:hover{
                background: #f6f6f6;
            }
        }
    }
</style>

關於樣式,這裡就不詳細展開了,只說其中幾個需要注意的點:

那個i元素的樣式,我用到了一個網路圖片,大家可以自行更換

待選選單ul在css裡並沒有讓它隱藏,因為我們要通過js來控制,具體原因見下文

待選選單ul使用了絕對定位,因為當它展開的時候,不應該影響頁面上其他元素的佈局

現在這個元件大概長這個樣子:

vue下拉列表的兩種實現方式比較

我們繼續為這個元件定義屬性,很顯然,待選選單應該作為屬性傳進來,一定不能是內部寫死的,屬性定義如下:

<script>
export default {
    name: "DropDownList",props:{
        dataList:{
            type:Array,default(){
                return [
                    {name: "選項一"},{name: "選項二"}
                ]
            }
        },labelProperty:{
            type:String,default(){ return "name" }
        }
    },data(){
        return {
            activeIndex:0
        }
    },}

其中dataList就是待選選單的資料來源屬性,這裡我們給這個屬性定義了預設值,這也是筆者建議大家養成的一個習慣,作為一個元件,最好有預設值,因為當別人使用你的元件時,可以先不設定相關屬性,就能看到一個成品的效果,也能快速檢視你這個元件所需屬性的資料細節。

另外一個屬性是labelProperty,這個屬性的作用是什麼?我們實際專案中的資料來源,並不一定都含有name這個欄位,因此就可能導致下拉選單無法渲染資料的文字,於是我們定義了這個屬性用來指定實際資料來源渲染文字的欄位,這個欄位必須是字串。這個屬性的預設值是name,因為它需要和預設資料來源保持一致。相信你還看到了一個元件內部資料,activeIndex,這個是用來表示當前選中項的索引的,我們後面會用到。

現在我們就可以在其他地方引入並使用這個元件了,雖然它還沒有完成,但我們不妨先讓它顯示在介面上吧:

<template>
  <div class="home">
    <DropList :dataList="dplist" labelProperty="city" @change="owww.cppcns.comnDpChange($event)"></DropList>
    <p>其他文字內容</p>
  </div>
</template>
<script>
  import DropList from '@/components/DropDownList.vue'
  //其他程式碼略
</script>

這個頁面引入並使用了我們的DropDownList元件,:dataList="dplist" 綁定了當前頁面的dplist陣列到元件的dataList屬性上,這個陣列中的物件有一個city欄位,我們希望此欄位顯示在下拉選單上,因此我們設定元件的labelProperty為city,我們還給這個元件註冊了change事件,這個元件內部需要派發這個事件,見下文。

現在我們回到元件的模板部分,發現它都還是靜態內容,我們把這些靜態內容修改為通過屬性渲染。

<template>
    <div class="zq-drop-list" @mouseover="onDplOver($event)" @mouseout="onDplOut($event)">
       程式設計客棧 <span>{{dplLable}}<i></i></span>
        <ul>
            <li v-for="(item,index) in dataList" :key="index" @click="onLiClick(index,$event)">{{item[labelProperty]}}</li>
        </ul>
    </div>
</template>

其中待選選單li的文字是 item[labelProperty] 這樣就能正確的顯示開發者指定的欄位了。

我們看看選中項的文字表示式:dplLabel,我們並沒有定義這個屬性,也沒有定義這個內部資料,它是哪兒來的?選中項的文字應該是 dataList[activeIndex][labelProperty] (這個很好理解吧,有問題請留言),但這個表示式太長了,寫在模板裡不利於維護,我們就把它寫到計算屬性裡吧。

computed:{
        dplLable(){
            return this.dataList[this.activeIndex][this.labelProperty]
        }
    }

於是才有了上面的dplLabel,計算屬性真的很好用呢。

現在下拉選單的檢視和資料關聯部分我們已經寫完了,接下來我們要實現它的功能。

第一步是先讓待選選單預設隱藏起來,這裡我們為什麼不直接用css的display:none呢,然後滑鼠經過的時候display:block不就可以了嗎?因為這樣的話,我們無法實現點選待選選單條目的時候讓它隱藏,體驗不好。我們用js來控制,但vue對直接訪問dom元素支援的並不好,我們要想在元件初始化的時候訪問dom元素,有一個最方便的做法,那就是:自定義指令。

我們為下拉選單元件新增區域性自定義指令,程式碼如下:

directives:{
        dpl:{
  程式設計客棧          bind(el){
                el.style.display = "none";
            }
        }
    },

這個dpl就是自定義指令啦,請忽略我笨拙的命名哈!然後我們在自定義指令的鉤子函式bind方法中,訪問el元素,控制它的style屬性display:none; 最後,把這個自定義指令加到模板裡面的ul標籤上。別忘了要加v-,現在看看效果,待選選單已經隱藏了。

<ul v-dpl>

我們利用自定義指令鉤子函式訪問dom元素,實現了對dom的控制,這一點非常實用!

讓我們繼續實現最開始為下拉選單定義的滑鼠經過和滑鼠滑出的監聽,實現待選選單的顯示與隱藏。

onDplOver(event){
    let ul = event.currentTarget.childNodes[1];
    ul.style.display = "block";
},onDplOut(event){
    let ul = event.currentTarget.childNodes[1];
    ul.style.display = "none";
},

我們在滑鼠事件中,訪問event的currentTarget物件,為什麼不是target?因為下拉選單的子元素也會觸發這個事件,如果訪問target,可能不會是我們預期的頂層元素。

最後一步,我們實現待選選單條目的點選事件,點選後,待選選單隱藏,修改內部狀態,派發change事件。

onLiClick(index){
    let path = event.path || (event.composedPath && event.composedPath()) //相容火狐和safari
    path[1].style.display = "none";
    this.activeIndex = index;
    this.$emit("change",{
        index:index,value:this.dataList[index]
    })
}

這裡有一個細節需要注意,我們要通過li元素找到外層ul元素,但path不支援火狐和safari,好在這兩個瀏覽器支援composedPath,因此才有了第一行程式碼的相容寫法。然後通過修改內部資料activeIndex實現選中項文字的更新,最後呼叫emit方法向父元素派發change事件,別忘了把事件物件封裝好傳出去。

完整的程式碼如下:

<template>
    <div class="zq-drop-list" @mouseover="onDplOver($event)" @mouseout="onDplOut($event)">
        <span>{{dplLable}}<i></i></span>
        <ul v-dpl>
            <li v-for="(item,$event)">{{item[labelProperty]}}</li>
        </ul>
    </div>
</template>
<script>
export default {
    name: "DropDownList",directives:{
        dpl:{
            bind(el){
                el.style.display = "none";
            }
        }
    },methods:{
        onDplOver(event){
            let ul = event.currentTarget.childNodes[1];
            ul.style.display = "block";
        },onDplOut(event){
            let ul = event.currentTarget.childNodes[1];
            ul.style.display = "none";
        },onLiClick(index){
            let path = event.path || (event.composedPath && event.composedPath()) //相容火狐和safari
            path[1].style.display = "none";
            this.activeIndex = index;
            this.$emit("change",{
                index:index,value:this.dataList[index]
            })
        }
    },computed:{
        dplLable(){
            return this.dataList[this.activeIndex][this.labelProperty]
        }
    }
}
&www.cppcns.comlt;/script>
<style scoped lang="scss">
    .zq-drop-list{
        display: inline-block;
        min-width: 100px;
        position: relative;
        span{
            display: block;
            height: 30px;
            line-height: 30px;
            background: #f1f1f1;
            font-size: 14px;
            text-align: center;
            color: #333333;
            border-radius: 4px;
            i{
                background: url(https://www.easyicon.net/api/resizeApi.php?id=1189852&size=16) no-repeat center center;
                margin-left: 6px;
                display: inline-block;
            }
        }
        ul{
            position: absolute;
            top: 30px;
            left: 0;
            width: 100%;
            margin: 0;
            padding: 0;
            border: solid 1px #f1f1f1;
            border-radius: 4px;
            overflow: hidden;
            li{
                list-style: none;
                height: 30px;
                line-height: 30px;
                font-size: 14px;
                border-bottom: solid 1px #f1f1f1;
                background: #ffffff;
            }
            li:last-child{
                border-bottom: none;
            }
            li:hover{
                background: #f6f6f6;
            }
        }
    }
</style>

以上為大家展示了vue如何實現一個下拉選單元件,雖然比較簡單,但也基本涉及到了元件開發常用的一些特性。希望能給大家一個參考,也希望大家多多支援我們。