用vue優雅地編寫UI組件的幾條指導原則

前言

最近在嘗試寫幾個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-selectyy-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複製代碼

那麼,在vueVNode進行patch的時候,yy-option做爲插槽被插到的是yy-scroll-bar裏面,此時yy-optionvue實例的父實例(也就是經過this.$parent獲取到的)是yy-scroll-barvue實例,而不是yy-selectvue實例
在寫組件的過程當中,yy-select實例充當一個父親,負責yy-inputyy-option這兩個組件的實例相互通訊,這個時候能夠人爲構造一個父子關係,即:在yy-option中找到yy-select這個實例,並設置this.parent爲該實例,同理在yy-select中找到全部的yy-option實例,並賦值爲一個數組:this.options。這個過程能夠在yy-option實例的created鉤子函數中完成。bash

只在內部改變本身的狀態

場景:在下拉菜單的輸入框裏面輸入文字,只有匹配到的option纔會顯示在列表裏。
作法:在yy-select組件中監聽yy-inputinput事件,而後寫個方法判斷this.options裏面有哪些知足要求,並設置this.options[i].visible=True
這種在父組件中直接修改子組件屬性的作法有諸多很差的地方。更合理的作法是將判斷的方法放到yy-option中,經過computed watch監聽父組件的一個比方說叫inputContent的屬性,又或者利用事件訂閱讓父組件經過分發事件,來調用yy-option的方法 ,讓yy-option在本身內部改變本身的visible屬性(有種相似vuex的感受,組件能夠改變store,但不能在組件的方法裏直接去改,得發個action,在規定的mutation裏面改)dom

data設置原子屬性,computed設置派生屬性

這一條和上一條形式上一致,只是看問題的角度不一樣。vue的核心思想是組件化數據驅動,這意味着對於全部的顯示(class,style),都要經過數據驅動,而不是直接操做dom。我在寫UI組件的時候一個主要的思考就是如何可以減小data中的變量,畢竟爲每個狀態設置一個變量去控制很冗餘。好比說yy-option,根據輸入框內容決定是否顯示須要visible,鼠標點擊了背景要變藍須要selected,經過鼠標或者鍵盤上下鍵停留背景要變灰hover,這樣就有三個變量放在data裏面,更糟糕的是若是你在yy-select父組件中經過邏輯判斷後直接修改這些變量值,這是一種很是ugly的作法。個人建議是在子組件的data中設置一些原子屬性,好比該optionvalue label以及在父組件的this.options中的index;在父組件中的data中設置value hoverIndex,那麼子組件中相似hover這樣的派生變量就能夠放到computed裏面,由hoverIndexindex派生獲得,它從原來的由父組件野蠻入侵式地修改變成了如今優雅地依賴於父組件函數

能用css控制的屬性、狀態就不要用js

說明:通常下拉菜單的輸入框後面都會跟一個向下的箭頭之類的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的設計,代碼的質量,要關注的可能就不只僅是一個一個孤立的知識點,而是,一個面。

相關文章
相關標籤/搜索