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

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 布局相关的东西。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容