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

相关文章
相关标签/搜索