Javascript编码三大原则(2) | 青训营笔记

41 阅读2分钟

这是我参与「第五届青训营 」笔记创作活动的第8天. 今天学习Javascript编码的三大原则中的组件封装.

组件封装

组件是指在web页面上抽象出来的包含 模板(HTML), 功能(JS) 和样式(CSS)的单元. 好的组件具有封装性, 正确性, 拓展性, 复用性.

以轮播图为例, 对组件封装进行进一步的说明.

例子

结构HTML

轮播图实际上就是一个无序列表.

表现CSS

实现轮播效果

  • 使用CSS绝对定位将图片重叠在同一个位置
  • 使用修饰符(modifier)来切换图片的状态
  • 使用CSS的transition来实现切换的效果

行为JS

用JavaScript实现切换的行为.

功能API

首先定义一个Slide的类, 在类里面实现下面几个API.

  • getSelectedItem() 获取当前选中的元素
  • getSelectedItemIndex() 得到当前选中元素在列表里的下标
  • slideTo() 滑动到特定下标的元素上
  • slideNext() 轮播下一张图片
  • slidePrevious() 轮播上一张图片
class Slider{
  constructor(id){
    this.container = document.getElementById(id);
    this.items = this.container
      .querySelectorAll('.slider-list__item, .slider-list__item--selected'); /* li的元素作为item */
  }
  getSelectedItem(){
    const selected = this.container
    .querySelector('.slider-list__item--selected');
    return selected
  }
  getSelectedItemIndex(){
    return Array.from(this.items).indexOf(this.getSelectedItem());
  }
  slideTo(idx){
    /* 得到当前被选中元素 */
    const selected = this.getSelectedItem();
    /* 当前元素改变为非选择状态 */
    if(selected){ 
      selected.className = 'slider-list__item';
    }
    /* 目标状态 */
    const item = this.items[idx];
    if(item){
      item.className = 'slider-list__item--selected';
    }
  }
  slideNext(){
    const currentIdx = this.getSelectedItemIndex();
    const nextIdx = (currentIdx + 1) % this.items.length; 
    this.slideTo(nextIdx);
  }
  slidePrevious(){
    const currentIdx = this.getSelectedItemIndex();
    const previousIdx = (this.items.length + currentIdx - 1)
    % this.items.length;
    this.slideTo(previousIdx);  
  }
}
​
const slider = new Slider('my-slider');
slider.slideTo(3);

控制流Event

状态绑定的效果一般使用自定义事件来实现.

constructor(id, cycle = 3000){
    this.container = document.getElementById(id);
    this.items = this.container.querySelectorAll('.slider-list__item, .slider-list__item--selected');
    this.cycle = cycle;
    /*小圆点对应的控件*/
    const controller = this.container.querySelector('.slide-list__control');
    if(controller){
      const buttons = controller.querySelectorAll('.slide-list__control-buttons, .slide-list__control-buttons--selected');
      /* 判断鼠标位于第几个小圆点上,把图片slide过去 */
      controller.addEventListener('mouseover', evt=>{
        const idx = Array.from(buttons).indexOf(evt.target);
        if(idx >= 0){
          this.slideTo(idx);
          this.stop();/* 停止自动播放 */
        }
      });
      /* 鼠标离开,开始自动播放 */
      controller.addEventListener('mouseout', evt=>{
        this.start();
      });
      /* 自定义事件slide,设置小圆点选中的状态 */
      this.container.addEventListener('slide', evt => {
        const idx = evt.detail.index
        const selected = controller.querySelector('.slide-list__control-buttons--selected');
        if(selected) selected.className = 'slide-list__control-buttons';
        buttons[idx].className = 'slide-list__control-buttons--selected';
      })
    }

左右两个按键

/*左键*/
const previous = this.container.querySelector('.slide-list__previous');
    if(previous){
      previous.addEventListener('click', evt => {
        this.stop();
        this.slidePrevious();
        this.start();
        evt.preventDefault();
      });
    }
/*右键*/    
const next = this.container.querySelector('.slide-list__next');
    if(next){
      next.addEventListener('click', evt => {
        this.stop();
        this.slideNext();
        this.start();
        evt.preventDefault();
      });
    }
  }

slideTo()进行了修改

slideTo(idx){
    let selected = this.getSelectedItem();
    if(selected){ 
      selected.className = 'slider-list__item';
    }
    let item = this.items[idx];
    if(item){
      item.className = 'slider-list__item--selected';
    }
  
    const detail = {index: idx}
    const event = new CustomEvent('slide', {bubbles:true, detail}) /* 新建一个事件,使得constructor里面可以监听slide事件*/
    this.container.dispatchEvent(event)
  }

增加了start和stop两个API

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

小结

组件封装的主要步骤为: (1)结构设计;(2)展示效果设计; (3)行为设计, 包含API和Event两部分.

重构 : 插件化

上面说到的组件不够灵活, 例如不要左右按键, 则需要对应修改html, css, js代码. 因此可以考虑将控制元素抽取成插件. 通过依赖注入的方式建立插件和组件之间的联系.

把三个部分的空间分别写在函数里面, 再通过依赖注入的方式, 在slide里面注入(把定义好的slide组件传进函数.