vue下拉列表的兩種實(shí)現(xiàn)方式比較
vue下拉列表的兩種實(shí)現(xiàn)
第一種采用v-for的方式
<el-select v-model="form.columeType" placeholder="字段類型">
<el-option v-for="(item,index) in columeTypeArr" :key="index" :label="item.label" :value="item.value">
</el-option>
</el-select>
這種方式需要在data中定義columeTypeArr,如下
data(){
return {
columeTypeArr:[{
value:'String',
label:'字符串'
},{
value:'Int',
label:'整數(shù)',
},{
value:'Decimal',
label:'數(shù)值型'
}],
}
}
第二種采用寫死的方式
直接在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中進(jìn)行配置,對于需要數(shù)據(jù)從后臺(tái)回顯的情況,明顯是第一種方法好。
對于簡單的下拉列表參數(shù)很少的情況,第二種明顯更占優(yōu)。
vue下拉菜單組件的實(shí)現(xiàn)
我們一起來實(shí)現(xiàn)一個(gè)vue的下拉菜單組件。
像這種基本UI組件,網(wǎng)上已經(jīng)有很多了,為什么要自己實(shí)現(xiàn)呢?其實(shí)并不是有意重復(fù)造輪子,而是想通過這個(gè)過程回顧一下vue組件開發(fā)的一些細(xì)節(jié)和注意事項(xiàng)。
為什么選擇下拉菜單組件?
因?yàn)椋郝槿鸽m小五臟俱全,這個(gè)小小的組件涉及到了不少vue組件開發(fā)的知識(shí)點(diǎn)。
好了,那就開始吧!
首先創(chuàng)建一個(gè)vue-cli的項(xiàng)目,筆者用的是vue-cli3,創(chuàng)建過程略,然后創(chuàng)建一個(gè)vue組件:DropDownList.vue
在編寫模板之前,我們來分析一下這個(gè)組件的視圖結(jié)構(gòu)和功能。
下拉菜單組件應(yīng)該由兩部分組成:
選中項(xiàng)的文本
待選菜單(默認(rèn)隱藏)
它的主要功能包括:
鼠標(biāo)經(jīng)過下拉菜單組件,顯示待選菜單
鼠標(biāo)滑出下拉菜單組件,隱藏待選菜單
鼠標(biāo)點(diǎn)擊待選菜單中的條目,選中項(xiàng)文本更新,組件派發(fā)change事件
我們編寫如下這樣的模板:
<template>
<div class="zq-drop-list" @mouseover="onDplOver($event)" @mouseout="onDplOut($event)">
<span>選中項(xiàng)的文本<i></i></span>
<ul>
<li>北京</li>
<li>上海</li>
<li>廣州</li>
</ul>
</div>
</template>
選中項(xiàng)文本右側(cè)的i標(biāo)簽,用來實(shí)現(xiàn)下拉菜單的三角形圖標(biāo),在下文的css中我們用背景圖來實(shí)現(xiàn)。
我們給根元素div已經(jīng)添加了鼠標(biāo)經(jīng)過和滑出的回調(diào)函數(shù),具體實(shí)現(xiàn)見下文。
接下來我們?yōu)檫@個(gè)下拉菜單編寫樣式,在模板下方添加style標(biāo)簽,為了防止和其他組件的樣式發(fā)生沖突,筆者建議大家在開發(fā)組件時(shí),都給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>
關(guān)于樣式,這里就不詳細(xì)展開了,只說其中幾個(gè)需要注意的點(diǎn):
那個(gè)i元素的樣式,我用到了一個(gè)網(wǎng)絡(luò)圖片,大家可以自行更換
待選菜單ul在css里并沒有讓它隱藏,因?yàn)槲覀円ㄟ^js來控制,具體原因見下文
待選菜單ul使用了絕對定位,因?yàn)楫?dāng)它展開的時(shí)候,不應(yīng)該影響頁面上其他元素的布局
現(xiàn)在這個(gè)組件大概長這個(gè)樣子:

我們繼續(xù)為這個(gè)組件定義屬性,很顯然,待選菜單應(yīng)該作為屬性傳進(jìn)來,一定不能是內(nèi)部寫死的,屬性定義如下:
<script>
export default {
name: "DropDownList",
props:{
dataList:{
type:Array,
default(){
return [
{name: "選項(xiàng)一"},
{name: "選項(xiàng)二"}
]
}
},
labelProperty:{
type:String,
default(){ return "name" }
}
},
data(){
return {
activeIndex:0
}
},
}
其中dataList就是待選菜單的數(shù)據(jù)源屬性,這里我們給這個(gè)屬性定義了默認(rèn)值,這也是筆者建議大家養(yǎng)成的一個(gè)習(xí)慣,作為一個(gè)組件,最好有默認(rèn)值,因?yàn)楫?dāng)別人使用你的組件時(shí),可以先不設(shè)置相關(guān)屬性,就能看到一個(gè)成品的效果,也能快速查看你這個(gè)組件所需屬性的數(shù)據(jù)細(xì)節(jié)。
另外一個(gè)屬性是labelProperty,這個(gè)屬性的作用是什么?我們實(shí)際項(xiàng)目中的數(shù)據(jù)源,并不一定都含有name這個(gè)字段,因此就可能導(dǎo)致下拉菜單無法渲染數(shù)據(jù)的文本,于是我們定義了這個(gè)屬性用來指定實(shí)際數(shù)據(jù)源渲染文本的字段,這個(gè)字段必須是字符串。這個(gè)屬性的默認(rèn)值是name,因?yàn)樗枰湍J(rèn)數(shù)據(jù)源保持一致。相信你還看到了一個(gè)組件內(nèi)部數(shù)據(jù),activeIndex,這個(gè)是用來表示當(dāng)前選中項(xiàng)的索引的,我們后面會(huì)用到。
現(xiàn)在我們就可以在其他地方引入并使用這個(gè)組件了,雖然它還沒有完成,但我們不妨先讓它顯示在界面上吧:
<template>
<div class="home">
<DropList :dataList="dplist" labelProperty="city" @change="onDpChange($event)"></DropList>
<p>其他文本內(nèi)容</p>
</div>
</template>
<script>
import DropList from '@/components/DropDownList.vue'
//其他代碼略
</script>
這個(gè)頁面引入并使用了我們的DropDownList組件,:dataList="dplist" 綁定了當(dāng)前頁面的dplist數(shù)組到組件的dataList屬性上,這個(gè)數(shù)組中的對象有一個(gè)city字段,我們希望此字段顯示在下拉菜單上,因此我們設(shè)置組件的labelProperty為city,我們還給這個(gè)組件注冊了change事件,這個(gè)組件內(nèi)部需要派發(fā)這個(gè)事件,見下文。
現(xiàn)在我們回到組件的模板部分,發(fā)現(xiàn)它都還是靜態(tài)內(nèi)容,我們把這些靜態(tài)內(nèi)容修改為通過屬性渲染。
<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] 這樣就能正確的顯示開發(fā)者指定的字段了。
我們看看選中項(xiàng)的文本表達(dá)式:dplLabel,我們并沒有定義這個(gè)屬性,也沒有定義這個(gè)內(nèi)部數(shù)據(jù),它是哪兒來的?選中項(xiàng)的文本應(yīng)該是 dataList[activeIndex][labelProperty] (這個(gè)很好理解吧,有問題請留言),但這個(gè)表達(dá)式太長了,寫在模板里不利于維護(hù),我們就把它寫到計(jì)算屬性里吧。
computed:{
dplLable(){
return this.dataList[this.activeIndex][this.labelProperty]
}
}
于是才有了上面的dplLabel,計(jì)算屬性真的很好用呢。
現(xiàn)在下拉菜單的視圖和數(shù)據(jù)關(guān)聯(lián)部分我們已經(jīng)寫完了,接下來我們要實(shí)現(xiàn)它的功能。
第一步是先讓待選菜單默認(rèn)隱藏起來,這里我們?yōu)槭裁床恢苯佑胏ss的display:none呢,然后鼠標(biāo)經(jīng)過的時(shí)候display:block不就可以了嗎?因?yàn)檫@樣的話,我們無法實(shí)現(xiàn)點(diǎn)擊待選菜單條目的時(shí)候讓它隱藏,體驗(yàn)不好。我們用js來控制,但vue對直接訪問dom元素支持的并不好,我們要想在組件初始化的時(shí)候訪問dom元素,有一個(gè)最方便的做法,那就是:自定義指令。
我們?yōu)橄吕藛谓M件添加局部自定義指令,代碼如下:
directives:{
dpl:{
bind(el){
el.style.display = "none";
}
}
},
這個(gè)dpl就是自定義指令啦,請忽略我笨拙的命名哈!然后我們在自定義指令的鉤子函數(shù)bind方法中,訪問el元素,控制它的style屬性display:none; 最后,把這個(gè)自定義指令加到模板里面的ul標(biāo)簽上。別忘了要加v-,現(xiàn)在看看效果,待選菜單已經(jīng)隱藏了。
<ul v-dpl>
我們利用自定義指令鉤子函數(shù)訪問dom元素,實(shí)現(xiàn)了對dom的控制,這一點(diǎn)非常實(shí)用!
讓我們繼續(xù)實(shí)現(xiàn)最開始為下拉菜單定義的鼠標(biāo)經(jīng)過和鼠標(biāo)滑出的監(jiān)聽,實(shí)現(xiàn)待選菜單的顯示與隱藏。
onDplOver(event){
let ul = event.currentTarget.childNodes[1];
ul.style.display = "block";
},
onDplOut(event){
let ul = event.currentTarget.childNodes[1];
ul.style.display = "none";
},
我們在鼠標(biāo)事件中,訪問event的currentTarget對象,為什么不是target?因?yàn)橄吕藛蔚淖釉匾矔?huì)觸發(fā)這個(gè)事件,如果訪問target,可能不會(huì)是我們預(yù)期的頂層元素。
最后一步,我們實(shí)現(xiàn)待選菜單條目的點(diǎn)擊事件,點(diǎn)擊后,待選菜單隱藏,修改內(nèi)部狀態(tài),派發(fā)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]
})
}
這里有一個(gè)細(xì)節(jié)需要注意,我們要通過li元素找到外層ul元素,但path不支持火狐和safari,好在這兩個(gè)瀏覽器支持composedPath,因此才有了第一行代碼的兼容寫法。然后通過修改內(nèi)部數(shù)據(jù)activeIndex實(shí)現(xiàn)選中項(xiàng)文本的更新,最后調(diào)用emit方法向父元素派發(fā)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, index) in dataList" :key="index" @click="onLiClick(index, $event)">{{item[labelProperty]}}</li>
</ul>
</div>
</template>
<script>
export default {
name: "DropDownList",
data(){
return {
activeIndex:0
}
},
props:{
dataList:{
type:Array,
default(){
return [
{name: "選項(xiàng)一"},
{name: "選項(xiàng)二"}
]
}
},
labelProperty:{
type:String,
default(){ return "name" }
}
},
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]
}
}
}
</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如何實(shí)現(xiàn)一個(gè)下拉菜單組件,雖然比較簡單,但也基本涉及到了組件開發(fā)常用的一些特性。希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue 在methods中調(diào)用mounted的實(shí)現(xiàn)操作
這篇文章主要介紹了vue 在methods中調(diào)用mounted的實(shí)現(xiàn)操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08
vue實(shí)現(xiàn)多個(gè)tab標(biāo)簽頁的切換與關(guān)閉詳細(xì)代碼
這篇文章主要給大家介紹了關(guān)于vue實(shí)現(xiàn)多個(gè)tab標(biāo)簽頁的切換與關(guān)閉的相關(guān)資料,使用vue.js實(shí)現(xiàn)tab切換很簡單,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10
在uni-app中使用element-ui的方法與報(bào)錯(cuò)解決
我們在開web開發(fā)的時(shí)候,經(jīng)常會(huì)使用到element或者uview-ui,下面這篇文章主要給大家介紹了關(guān)于在uni-app中使用element-ui的方法與報(bào)錯(cuò)解決的相關(guān)資料,需要的朋友可以參考下2022-04-04
Node.js使用orm2進(jìn)行update操作時(shí)關(guān)聯(lián)字段無法修改的解決方法
這篇文章主要給大家介紹了Node.js使用orm2進(jìn)行update操作時(shí)關(guān)聯(lián)字段無法修改的解決方法,文中給出了詳細(xì)的示例代碼供大家參考學(xué)習(xí),對大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-06-06
ToB項(xiàng)目如何沉淀業(yè)務(wù)公共組件示例詳解
這篇文章主要為大家介紹了ToB項(xiàng)目如何沉淀業(yè)務(wù)公共組件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10

