如何更优雅的封装组件 - Search

作者: Danile_1226 | 来源:发表于2019-06-30 11:23 被阅读3次

    Search - 搜索框无论在B端还是C端都会存在的东西。无疑把其抽离封装📦成则最为合适。
    能确保组件的易读性/可扩展性/最佳使用性,一直是我不断编码过程中思考的问题。
    此文章以 Taro + Ts + Redux 为技术栈作为分享。

    直接步入正题:
    先看三张张搜索框的UI设计稿。
    图一:【刚进入搜索页面中,输入框的样式】
    图二:【搜索框输入内容时,有输入框清除按钮的增加】
    图三:【搜索完成之后,数据展示】


    image.png image.png image.png

    思考一下你将会如何布局 -- 不知你们的代码是否和我最开始的一样/或者类似。
    组件分为search-left / search-right(btn)
    search-left 分为 image / input / image(clear)

    <View className="search">
      <View className="search-left">
        <Image src={search} className="search-left-img" />
        <Input
          placeholder={placeholder}
          className="search-left-input"
          onInput={onSearchName}
          value={searchVal}
          focus={foucus}
          type="text"
          confirm-type="search"
          onFocus={onFocus}
          onConfirm={onSearchList}
        />
        {
          showdelImg && (
            <Image src={inputClear} className="search-left-clear" onClick={onReset} />)
        }
      </View>
      <View className="search-btn" onClick={onSearchList}>搜索</View>
    </View>
    

    优化一:Input的属性排序过于混乱,我们先来调整一下。可以规定,所有都是先以 className为先,其次是一些带 string 属性,接下来是 value 值。最后可以放事件函数。

    <Input
      className="search-left-input"
      type="text"
      confirm-type="search"
      placeholder={placeholder}
      value={searchVal}
      focus={foucus}
      onInput={onSearchName}
      onFocus={onFocus}
      onConfirm={onSearchList}
    />
    

    优化二: 清楚的 X 的按钮,则用了 showdelImg 这个变量进行展示。我们试想一下,缺点在哪里。
    第一:组件多传一个变量。
    第二:在引用组件的页面中要根据不同状态进行改变(看如下事件 onFocus / onSearch),最后传入 Search 组件进行展示与否的渲染。
    第三:判断太多,对于后期维护不友好,并且如果嵌套太深,容易地狱。

    // Page 页面引用的地方,此出引用的属性最好与组件的顺序一直,这样方便知道少了什么传值。
    <Search
      placeholder="搜索店铺名称"
      onSearchName={e => this.onSearch(e)}
      onReset={this.onReset}
      onFocus={this.onFocus}
      onSearchList={this.onSearchList}
      searchVal={searchVal}
      showdelImg={showdelImg}
      foucus={foucus}
    />
    
    // input 获取焦点
    onFocus = () => {
      const { searchVal } = this.state;
      if (searchVal) {
        this.setState({ showdelImg: true });
      }
    }
    
    // 获取搜索的内容
    onSearch = (e) => {
      const val = e.detail.value;
      this.setState({
        searchVal: val,
        showdelImg: true,
      });
    };
    

    我们第二步 Search 组件中的 Image(clear)属性优化一下写法。
    思路:将 showdelImg 变量,该为组件内部去实现,通过加入内部

    // 在 render中用法
    const clearImgStyle = { display: 'flex' };
    if (!searchVal.length) {
      clearImgStyle.display = 'none';
    }
    
    // clear 处的优化写法
    <Image
      className="search-left-clear"
      style={clearImgStyle}
      src={inputClear}
      onClick={onReset}
    />
    

    优化三:思考一下,我们目前的代码,布局则就是左边是搜索的input框,右边是搜索的Button按钮。但是,这时候让你改成只有一条搜索框,右边的Button没有,。功能不变,样式变化,这样显得很尴尬。是重新写一个还是怎样别的实现方式?
    我第一想到的是,传入一个变量,这无疑是一种的方式。


    image.png image.png
    // 在render中,引入showActionButton变量
    const { showActionButton } = this.props;
    const actionStyle: { opacity?: number, marginRight?: string } = {};
    if(showActionButton) {
      actionStyle.opacity = 1;
      actionStyle.marginRight = `0`;
    }
    
    // 在搜索Button多加入一个style
    <View
      className="search-btn"
      style={actionStyle}
      onClick={onSearchList}
      >
      搜索
    </View>
    

    如上基本上是从html角度去优化,其还可以优化的点分为,Input 的默认值 placeholder 可以分出去。原因是,最开始默认值的样式与输入值的样式可能不一样,目前先不展开说明。

    优化四:写好CSS,先从组件的className着手,网上已经有很多命名规范等,这里不过多呈现。从最开始的代码看出来,基本上命名都是以 search-left-input 。可以优化为: search-left__input 一会儿在CSS代码中,大家就能体现这样的优势了。

    先附上一份,最原始的CSS代码,与本文最开始的TSX代码进行结合。

    .search {
      display: flex;
      padding: 10rpx 40rpx 20rpx 30rpx;
      background-color: #fff;
      position: fixed;
      z-index: 1;
      &-left {
        width: 576rpx;
        height: 80rpx;
        background: #f2f2f2;
        border-radius: 40rpx;
        display: flex;
        margin-right: 30rpx;
        &-img {
          width: 40rpx;
          height: 40rpx;
          padding: 20rpx 4rpx 20rpx 40rpx;
        }
        &-input {
          padding: 19rpx;
          width: 356rpx;
          height: 42rpx;
          font-size: 30rpx;
          color: #111111;
        }
        &-clear {
          width: 36rpx;
          height: 36rpx;
          padding: 22rpx 0 22rpx 35rpx;
        }
      }
    
      &-btn {
        width: 64rpx;
        height: 80rpx;
        line-height: 80rpx;
        font-size: 32rpx;
        color: #111111;
      }
    }
    

    其实整体看上去是没什么毛病,样式功能也都能实现,通过变量赋予不同的类名,在做样式的调整的思维。但是,可以更好,此处分享一篇文章。
    https://github.com/cssmagic/CSS-Secrets/issues/8
    如果要改一个 Button 的样式,则需要修改长宽高,border,背景色,垂直水平居中等属性。只是一个简简单单的 Button 可还好,但是更多的是若一个页面的布局写得不好,在往里面挪地儿,那可能就很难了。

    最后附上一份源码:
    思路一:default.scss 主要是维护整个小程序主题的颜色边距等(如下有)
    思路二:将一些字体大小,提取出来做变量
    思路三:共用的一些 mixins 可以提取出来。多在项目中沉淀。

    @import '../variables/default.scss';
    @import '../mixins/index.scss';
    
    $at-search-bar-font-size: 14PX;
    $at-search-bar-input-height: 30PX;
    $at-search-bar-input-padding: 25PX;
    $at-search-bar-btn-padding: 10PX;
    $at-search-bar-placeholder-padding: 12PX;
    $at-search-bar-input-bg-color: $color-bg-grey;
    $at-search-bar-input-color: $color-black-0;
    $at-search-bar-placholder-color: $color-grey-2;
    
    .at-search-bar {
      display: flex;
      align-items: center;
      padding: $spacing-v-sm $spacing-v-md;
      background-color: $color-bg;
      overflow: hidden;
      box-sizing: border-box;
      @include hairline-bottom();
    
      /* elements */
      &__input-cnt {
        position: relative;
        flex: 1;
        width: 100%;
        height: $at-search-bar-input-height;
        background-color: $at-search-bar-input-bg-color;
        border-radius: $at-search-bar-input-height / 2;
        overflow: hidden;
        display: flex;
      }
    
      &__input {
        position: absolute;
        display: block;
        top: 0;
        left: 0;
        width: 100%;
        height: $at-search-bar-input-height !important;
        padding: $at-search-bar-placeholder-padding $at-search-bar-input-padding + $at-search-bar-placeholder-padding;
        color: $at-search-bar-input-color;
        font-size: $at-search-bar-font-size !important;
        text-align: left;
        background-color: transparent;
        transition: width 0.3s;
        box-sizing: border-box;
      }
    
      &__clear {
        position: absolute;
        display: flex;
        align-items: center;
        justify-content: center;
        top: 0;
        right: $at-search-bar-placeholder-padding;
        height: $at-search-bar-input-height;
        width: $at-search-bar-input-padding;
        color: $at-search-bar-placholder-color;
        font-size: $at-search-bar-font-size;
        line-height: 0;
        vertical-align: middle;
      }
    
      &__action {
        flex: none;
        display: block;
        margin-left: 10PX;
        padding: 0 $at-search-bar-btn-padding;
        height: $at-search-bar-input-height;
        color: $at-search-bar-btn-color;
        font-size: $at-search-bar-font-size;
        line-height: $at-search-bar-input-height;
        border-radius: 4PX;
        background-color: $at-search-bar-btn-bg-color;
        transition: margin-right 0.3s, opacity 0.3s;
        opacity: 0;
      }
    
      /* modifiers */
      &--fixed {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        z-index: $zindex-search-bar;
      }
    }
    

    defult.scss 中的代码

    $hd: 2 !default; // 基本单位
    
    
    /* 垂直间距 */
    $spacing-v-xs: 5px * $hd !default;
    $spacing-v-sm: 10px * $hd !default;
    $spacing-v-md: 20px * $hd !default;
    $spacing-v-lg: 30px * $hd !default;
    $spacing-v-xl: 40px * $hd !default;
    
    /* 背景色 */
    $color-bg: #FFF !default;
    $color-bg-base: #FAFBFC !default;
    $color-bg-light: #ECF5FD !default;
    $color-bg-lighter: tint($color-bg-light, 50%) !default;
    $color-bg-grey: #F7F7F7 !default;
    

    mixins 中的代码

    @mixin hairline-bottom(
      $color: $color-border-light,
      $style: solid,
      $width: 1PX
    ) {
      position: relative;
    
      &::after {
        @include hairline-base($color, $style);
    
        border-bottom-width: $width;
      }
    }
    

    总结:虽然样式是整个项目中最简单的地方,但是,也绝对是耗时多,重复率最高的地方。如果后期可以思考如何将更优雅的写出样式,将代码可读性,更好的达到复用性。沉淀出一些常用的组件,一些常用的 mixins 以及 flex 布局相关的东西。

    相关文章

      网友评论

        本文标题:如何更优雅的封装组件 - Search

        本文链接:https://www.haomeiwen.com/subject/lafycctx.html