最近在尝试写几个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
的设计,代码的质量,要关注的可能就不只仅是一个一个孤立的知识点,而是,一个面。