最近在嘗試寫幾個UI組件,並經過閱讀element-ui的源碼,與其反覆比較,而後認真思考,最後總結出一些本身的一些心得和體會。在造輪子的過程當中,既鞏固了html
,css
,js
基礎,又加深了對vue
源碼的理解,更重要的是給了我一個溫習和實踐所學過的設計模式和思想的機會,來編寫更加優雅的代碼。
本文會介紹一些本身總結的,關於如何用vue
優雅地設計和編碼ui組件的指導原則,更多的是但願給你們提供一些選擇和參考,而不是墨守成規的條例。css
一個簡單的下拉菜單主要是一個輸入框和列表構成
在vue
中調用的方式大體是這樣的:html
<yy-select v-model="value" placeholder="請選擇">
<yy-option v-for="item in options" :value="item.value" :label="item.label">
</yy-option>
</yy-select>複製代碼
這裏有兩個組件,分別是yy-select
,yy-option
,在內部實現中,yy-option
能夠是一個<li>
標籤,yy-select
則包含一個<input>
標籤,並經過<slot>
將<li>
插入到一個<ul>
中。vue
輪播圖的原理是經過將多個div
放到一個父標籤div
(寬度width
)中,並將父標籤div
設置成overflow:hidden
,將當前顯示的div
設置成transformX(0)
,上一個div
設置成transformX(-width)
,下一個div
設置成transformX(width)
,再利用過渡便可實現切換的效果
一個簡單的輪播圖是由上面提到的父標籤div
,以及一些附加元素,好比左右兩邊放個左右切換按鈕,下面放一排指定元素切換按鈕構成
在vue
中調用的方式大體是這樣的:vuex
<yy-carousel>
<yy-carousel-item v-for="item in 4" >
<h3>{{item}}</h3>
</yy-carousel-item>
</yy-carousel>複製代碼
能夠發現調用方式和下拉菜單很相似,在內部實現中,yy-carousel-item
能夠是一個<div>
標籤,yy-carousel
則經過<slot>
將這些<div>
包含進來,並添加一些<button>
之類的標籤element-ui
接下來將開始個人表演設計模式
created
鉤子中創建「父子」關係這裏要先解釋一下vue
插槽原理:經過閱讀vue
源碼,咱們能夠知道,<yy-select><yy-option></yy-option></yy-select>
會建立兩個VNode
,就像那模板代碼給人的感受同樣,這個兩個VNode
是父子關係。然而,假設yy-select
組件中的<template>
是下面這樣的:數組
<template>
<yy-input></yy-input>
<yy-scroll-bar>
<slot></slot>
</yy-scroll-bar>
</template複製代碼
那麼,在vue
對VNode
進行patch
的時候,yy-option
做爲插槽被插到的是yy-scroll-bar
裏面,此時yy-option
的vue實例的父實例(也就是經過this.$parent
獲取到的)是yy-scroll-bar
的vue實例,而不是yy-select
的vue實例。
在寫組件的過程當中,yy-select
實例充當一個父親,負責yy-input
和yy-option
這兩個組件的實例相互通訊,這個時候能夠人爲構造一個父子關係,即:在yy-option
中找到yy-select
這個實例,並設置this.parent
爲該實例,同理在yy-select
中找到全部的yy-option
實例,並賦值爲一個數組:this.options
。這個過程能夠在yy-option
實例的created
鉤子函數中完成。bash
場景:在下拉菜單的輸入框裏面輸入文字,只有匹配到的option
纔會顯示在列表裏。
作法:在yy-select
組件中監聽yy-input
的input
事件,而後寫個方法判斷this.options
裏面有哪些知足要求,並設置this.options[i].visible=True
這種在父組件中直接修改子組件屬性的作法有諸多很差的地方。更合理的作法是將判斷的方法放到yy-option
中,經過computed watch
監聽父組件的一個比方說叫inputContent
的屬性,又或者利用事件訂閱讓父組件經過分發事件,來調用yy-option
的方法 ,讓yy-option
在本身內部改變本身的visible
屬性(有種相似vuex
的感受,組件能夠改變store
,但不能在組件的方法裏直接去改,得發個action
,在規定的mutation
裏面改)dom
這一條和上一條形式上一致,只是看問題的角度不一樣。vue
的核心思想是組件化和數據驅動,這意味着對於全部的顯示(class
,style
),都要經過數據驅動,而不是直接操做dom
。我在寫UI組件的時候一個主要的思考就是如何可以減小data
中的變量,畢竟爲每個狀態設置一個變量去控制很冗餘。好比說yy-option
,根據輸入框內容決定是否顯示須要visible
,鼠標點擊了背景要變藍須要selected
,經過鼠標或者鍵盤上下鍵停留背景要變灰hover
,這樣就有三個變量放在data
裏面,更糟糕的是若是你在yy-select
父組件中經過邏輯判斷後直接修改這些變量值,這是一種很是ugly的作法。個人建議是在子組件的data
中設置一些原子屬性,好比該option
的value label
以及在父組件的this.options
中的index
;在父組件中的data
中設置value hoverIndex
,那麼子組件中相似hover
這樣的派生變量就能夠放到computed
裏面,由hoverIndex
和index
派生獲得,它從原來的由父組件野蠻入侵式地修改變成了如今優雅地依賴於父組件函數
說明:通常下拉菜單的輸入框後面都會跟一個向下的箭頭之類的icon
,還能夠先後各放一些修飾的元素,按照組件化的設計思想,咱們將其封裝成一個對外暴露的<yy-input>
場景:鼠標移動到下拉菜單的yy-input
的<input>
輸入框,整個yy-input
的邊界顯示淺藍色;點擊按鈕顯示灰色;默認是淺灰色
方法:將<input>
和icon
放到一個<div>
裏面,監聽<input>
的hover focus
事件,來改變isHover isFocus
變量的值,從而改變<div>
的style
或者class
,達到修改border
的目的
你會發如今data
裏面添加了不少額外的變量,在不少業務邏輯裏面也要記得添加修改這些變量的語句(點擊輸入框後邊界變藍且彈出列表,此時isFocus=true
,點擊列表的某個option
後,記得設置isFocus=false
,要恢復成默認顏色)。有沒有一種方法能夠經過監聽<input>
的事件來修改整個邊界的顏色?
答案是css
。其實只要將icon
絕對定位到輸入框裏面就行了,這時候整個邊界的顏色,就是輸入框邊界的顏色……
總的來講,上面的幾條指導原則優化的目標主要是變量和通訊。如何肯定每一個組件內部所須要的變量,以及這些組件內部變量如何通訊,是用vue
優雅地編寫UI
組件的關鍵。
要把一件事情作出來是簡單的,好比寫個下拉菜單或者輪播圖,原理都不難。只是當考慮到API
的設計,代碼的質量,要關注的可能就不只僅是一個一個孤立的知識點,而是,一個面。