资讯专栏INFORMATION COLUMN

第八集: 从零开始实现一套pc端vue的ui组件库(input, textarea组件)

chanthuang / 2548人阅读

摘要:第八集从零开始实现输入框组件本集定位组件是交互的一大利器他与用户的交流最为密切所以奠定了他在组件界的重要地位也算是一种如果可以的话本集也会一起说完毕竟是一个类型的一起学完收获会很大古人云组件不封输入框,一到面试就发慌一简介大家如果对这个

第八集: 从零开始实现(输入框input,textarea组件)

本集定位:
input组件是交互的一大利器, 他与用户的交流最为密切, 所以奠定了他在组件界的重要地位.
textarea也算是一种input, 如果可以的话, 本集也会一起说完, 毕竟是一个类型的, 一起学完收获会很大.
古人云:"组件不封输入框,一到面试就发慌"

一. v-model 简介
大家如果对 v-model这个指令的原理不熟悉, 建议去学习下vue源码或者看看相关的分析文章, 很重要的知识, 封装组件多了就会知道这个指令真是太棒了! 这里我就简单说一下他的规则.
1: 父级在组件上绑定了v-model时, 其实就是在往组件里面传递value变量.
2: 你的组件在props上定义value, 就可以取到值.
3: 每当组件里this.$emit("input",n)往外面发送事件的时候, 外面会把这个n值 赋值给value
4: 这么设计的原因: 你在组件里面无权改变传入的值, 这个值你想改成什么值就要吐出去, 让外面改.

好了说了这么多开始实战吧!

二. 基本结构
vue-cc-ui/src/components/Input/index.js
老套路, 统一导出为了适配vue.use的使用方式

import Input from "./main/input.vue"

Input.install = function(Vue) {
  Vue.component(Input.name, Input);
};

export default Input

vue-cc-ui/src/components/Input/main/input.vue

type: 这个属性比较重要, 因为要通过它来区分input与textarea, 还可以为input指定number模式.

命名依然是bem

v-bind="$attrs" 解释下这个的意思, $attrs指的就是用户传进来的属性, 但是不包括我们组件内部用props接收的属性, 也不包括class style这种, 写它是为了用户可以传很多input原生的属性, 毕竟我们没必要把所有属性都做处理, 让组件保有原生功能.

placeholder这种模式基本也被现代抛弃了, 针对他也可以封装成一个具体的组件, 这个属性想调整属性实在是太困难了, 更别说我们现在还需要placeholder轮播,变色,点击等等效果.

vue 在行间写事件的时候, 事件对象会以$event的形式传给你使用, 其实从代码的角度来说, 是监控到你这里用了$event关键词,则把对应的参数赋值为事件对象.

props: {
    value: [String, Number],
    placeholder: [String, Number],
    type: {
      type: String,
      default: "text"
    }
  },

三. 丰富事件

输入框有很多种事件, 他们能给用户更好的体验性.

比如在手机端, 我们项目之前遇到的问题就是, 用户点击输入框的时候, 会弹出手机键盘, 但是弹出的键盘会把输入框顶上去, 某些型号的手机会出现, 就算输入完毕点击完成, 可是输入框还是被顶上去的状态, 后来我是借助blur 与 focus事件才兼容了这写手机

很多输入框也采取节流与防抖, 比如做搜索的相关模糊匹配

有些以搜索为主的页面, 需要自动聚焦

四. 各种状态

禁用状态, 置灰并且把鼠标变为禁止状态 (disabled)

只读, 并不置灰, 但是也不能改 (readonly)

具体样式会在后面出来详细解释

 

五. 为输入框添加状态, 并附上icon选项

很多输入框左右都要放个icon充充门面, 分为左侧与右侧icon

右侧icon允许输入文字, icon要有相应的点击效果

当组件为disabled状态的时候, icon也要相应的置灰

效果图



六. 清空按钮
现在的输入框基本都有这个清空按钮, 毕竟可以节省用的时间, 也算是个好功能,
当用户传入clear的时候会判断, 是否禁止修改, 框内是否有值, 是否是hover状态

hover事件放在父级上

 

清除事件, 对外返回空就ok

clickClear() {
      this.$emit("input", "");
      this.$emit("change", "");
    },

判断是否显示

  computed: {
    showClear() {
      if (
        this.clear &&      // 开启功能
        !this.disabled &&  // 不是禁用
        !this.readonly &&  // 不是只读
        this.value!== "" &&  // 不是空值
        (this.hovering || this.focus) // 聚焦或者hover状态下
      )return true;
      return false;
    }
  },

vue-cc-ui/src/style/Input.scss

// 引入老四样
@import "./common/var.scss";
@import "./common/extend.scss";
@import "./common/mixin.scss";
@import "./config/index.scss";
// 这里毕竟是两个月前写的组件, 命名方面不是很好, 接下来会统一改正
@include b(input) {
    cursor: pointer;
    position: relative;
    align-items: center;
    display: inline-flex; // 直接flex会独占一行
    background-color: white;
    transition: all .3s;
    @include b(input__inner) {
        border: none;
        flex: 1;
        width: 100%;
        font-size: 1em;
        padding: 9px 16px;
        &:focus { outline: 0; } // 这样写对障碍阅读不是很友好
        @include placeholder{ // placeholder设置颜色很头疼, 请看下面
            color: $--color-input-placeholder;
        }
    };
    @include b(input__prefix) {
        align-items: center;
        display: inline-flex;
        &:hover{transform: scale(1.1)}
        @include when(left) {
            padding-left:6px;
        }
        @include when(right) {
            padding-right:6px;
        }
    };
    @include b(input__clear){
        position: absolute;
        right: 24px;
        &:hover{ animation: size .5s infinite linear;}
    };
    @include b(input--input__disabled){
        @include commonShadow(disabled);
    };
    @at-root {
        @include b(input__normal){
            @include commonShadow($--color-black);
            &:hover {
                z-index: 6;
                transform: scale(1.2);
            }
        }
        @include b(input__error){
            @include commonShadow(danger);
        }
        @include b(input__abnormal){
            @include commonShadow($--color-black);
        }
    }
}

element 这个处理做的也不错

@mixin placeholder {
  &::-webkit-input-placeholder {
    @content;
  }

  &::-moz-placeholder {
    @content;
  }

  &:-ms-input-placeholder {
    @content;
  }
}

七. textarea 文本域

基本结构

在用户type输入的是textarea时候开启

把上面的基础功能复制下来, 直接放上就可以用的

textareaCalcStyle: 来设置他的宽高, 毕竟他与input不同, 可能需要很大面积

用户可以设置最大高度与最小高度

难点: 如果用户选择了自动适应高度那就麻烦了, 这个组件没有提供原生的解决方案, 第一版我是采用获取其高度进行运算得出来的, 但是及特殊的情况会有bug, 最后参考了element-ui的实现方式, 这里也让我学习到了.