【青训营】写好JS——组件封装(上)

461 阅读4分钟

这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战

嗟予贫贱年将老,学古忧时满怀抱。

组件是指Web页面上抽出来一个个包含模版(HTML)、功能(JS)和样式(CSS)的单元。好的组件具备封装性、正确性、扩展性、复用性。

例子:轮播图

如果要你用原生JS写一个电商或视频网站上常见的轮播图,应该怎么实现?怎么封装成组件?怎么让它的扩展性和复用性更高?

轮播.gif

1. 结构设计

轮播图是一个典型的列表结构,我们可以用无序列表ul>li*4来实现,然后根据效果图插入4张图片即可:

carbon (1)

这里我们使用的命名是一种CSS的书写规范,叫做BEM,是英文Block-Element-Modifier的简写,这一规范采用三个部分来描述规则,其中:

  • __:双下划线用来连接块和块的子元素
  • -:单中划线仅作为连字符使用,连接块或元素或修饰符的多个单词
  • --:双中划线用来连接块或元素的状态

这样命名可以将用户界面分隔为独立的块,从而使开发复杂的UI界面变得更简单,代码可读性也更强。

上面的HTML结构中,我们使用a元素分别表示上一张和下一张:

<a class="slider__previous"></a>
<a class="slider__next"></a>

span*4表示底部的四个小圆点:

<div class="slider__control">
  <span class="slider__control-buttons--selected"></span>
  <span class="slider__control-buttons"></span>
  <span class="slider__control-buttons"></span>
  <span class="slider__control-buttons"></span>
</div>

2. 展现效果

根据效果图我们继续添加CSS样式,首先设置宽高,隐藏列表样式:

.slider {
  position: relative;
  width: 790px;
  height: 340px;
}

.slider ul {
  list-style-type:none;
  position: relative;
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
}

将图片重叠,只显示.slider__item--selected

.slider__item,
.slider__item--selected {
  position: absolute;
  transition: opacity 1s;
  opacity: 0;			/* 完全透明 */
  text-align: center;
}

.slider__item--selected {
  transition: opacity 1s;
  opacity: 1;
}

能够上下切换的左右箭头:

carbon (2)

最后定义底部的4个小圆点:

carbon (3)

3. API设计

API的设计应保证原子操作,职能单一,满足灵活性。

我们先简单分析一下需求:

  1. 图片循环播放,每张停留若干时间
  2. 点击左右箭头可以上下切换
  3. 鼠标悬浮在底部小圆点上时会跳到对应图片
  4. 小圆点也会随着图片滚动

根据需求和月影大大的设计要求,我们设计了几个组件API:

carbon (6)

将其封装成一个类:

carbon (8)

然后通过setInterval()实现循环播放,间隔为3秒:

const container = document.querySelector('.slider');
const slider = new Slider(container);

setInterval(() => {
  slider.slideNext();
}, 3000);

4. 用户控制

在JS代码中,一个方法一般来说最多只能有15行代码,超过了就需要重构。

实现了API后,我们需要实现用户控制,根据需求修改构造器:

carbon (10).png

用定时器来实现stop()start(),当鼠标移动到小圆点上(mouseover),判断是第几个小圆点,停止循环播放,切换到对应图片,当鼠标移出后(mouseout)再开始循环。

start() {
  this.stop();
  this._timer = setInterval(() => this.slideNext(), this.cycle);
}

stop() {
  clearInterval(this._timer);
}

然后我们处理自定义的slide事件,修改slideTo()

slideTo(idx) {
  
  ...

  // 触发自定义事件slide
  const detail = {index: idx};
  const event = new CustomEvent('slide', {bubbles: true, detail});
  this.container.dispatchEvent(event);
}

最后将调用过程修改一下,组件的全部功能就完成了。

const container = document.querySelector('.slider');
const slider = new Slider({container});
slider.start();

通过轮播图组件编写过程,我们可以总结一下组件设计的一般性步骤:

  1. 结构设计和展现效果(HTML&CSS)
  2. 设计组件API
  3. 设计用户控制流程

我们实现的轮播组件实现了封装性和正确性,但是缺少了可扩展性,这个组件只能满足自身的使用,很难扩展到其他的组件。而且当有功能变化时,也需要修改其自身内部的代码。

比如当产品经理希望将图片下方的小圆点去掉只保留左右箭头时,在这个版本中我们就需要注释或修改所有与小圆点相关的代码,而且当有新的用户控制需求时,我们都需要涉及核心代码的修改,所以如何避免这样的修改,让组件具备可扩展性和复用性就是我们下节要做的事情了。