跟着月影学JavaScript(下)|青训营笔记

67 阅读2分钟

这是我参与「第四届青训营」笔记创作活动的第四天

一:组件封装案例——轮播图

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

1.结构:HTML

 <div id="my-slider" class="slider-list">
      <ul>
        <li class="slider-list__item--selected">
          <img src="https://p5.ssl.qhimg.com/t0119c74624763dd070.png">
        </li>
        <li class="slider-list__item">
          <img src="https://p4.ssl.qhimg.com/t01adbe3351db853eb3.jpg">
        </li>
        <li class="slider-list__item">
          <img src="https://p2.ssl.qhimg.com/t01645cd5ba0c3b60cb.jpg">
        </li>
        <li class="slider-list__item">
          <img src="https://p4.ssl.qhimg.com/t01331ac159b58f5478.jpg">
        </li>
      </ul>
    </div>

2.表现:CSS

  • 使用 CSS 绝对定位将图片重叠在同一个位置
  • 轮播图切换的状态使用修饰符(modifier)
  • 轮播图的切换动画使用 CSS transition
#my-slider{
    position: relative;
    width: 790px;
  }

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

  .slider-list__item,
  .slider-list__item--selected{
    position: absolute;
    transition: opacity 1s;
    opacity: 0;
    text-align: center;
  }

  .slider-list__item--selected{
    transition: opacity 1s;
    opacity: 1;
  }

3.行为:JS

<a class="slide-list__next"></a>
  <a class="slide-list__previous"></a>
  <div class="slide-list__control">
    <span class="slide-list__control-buttons--selected"></span>
    <span class="slide-list__control-buttons"></span>
    <span class="slide-list__control-buttons"></span>
    <span class="slide-list__control-buttons"></span>
  </div>
  const detail = {index: idx}
  const event = new CustomEvent('slide', {bubbles:true, detail})
  this.container.dispatchEvent(event)

二:轮播图改进

1.重构:插件化

  • 将控制元素抽取成插件
  • 插件与组件之间通过依赖注入方式建立联系
function pluginController(slider){
    const controller = slider.container.querySelector('.slide-list__control');
    if(controller){
      const buttons = controller.querySelectorAll('.slide-list__control-buttons, .slide-list__control-buttons--selected');
      controller.addEventListener('mouseover', evt=>{
        const idx = Array.from(buttons).indexOf(evt.target);
        if(idx >= 0){
          slider.slideTo(idx);
          slider.stop();
        }
      });

      controller.addEventListener('mouseout', evt=>{
        slider.start();
      });

      slider.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';
      });
    }  
  }

  function pluginPrevious(slider){
    const previous = slider.container.querySelector('.slide-list__previous');
    if(previous){
      previous.addEventListener('click', evt => {
        slider.stop();
        slider.slidePrevious();
        slider.start();
        evt.preventDefault();
      });
    }  
  }

  function pluginNext(slider){
    const next = slider.container.querySelector('.slide-list__next');
    if(next){
      next.addEventListener('click', evt => {
        slider.stop();
        slider.slideNext();
        slider.start();
        evt.preventDefault();
      });
    }  
  }

2.重构:模板化

  • 将HTML模板化,更易于扩展
class Slider{
    constructor(id, opts = {images:[], cycle: 3000}){
      this.container = document.getElementById(id);
      this.options = opts;
      this.container.innerHTML = this.render();
      this.items = this.container.querySelectorAll('.slider-list__item, .slider-list__item--selected');
      this.cycle = opts.cycle || 3000;
      this.slideTo(0);
    }
    render(){
      const images = this.options.images;
      const content = images.map(image => `
        <li class="slider-list__item">
          <img src="${image}">
        </li>    
      `.trim());
      
      return `<ul>${content.join('')}</ul>`;
    }
  }

三:组件封装的总结:

  • 组件设计的原则:封装性、正确性、扩展性、复用性

  • 实现组件的步骤:结构设计、展现效果、行为设计

  • 三次重构

    1. 插件化
    2. 模板化
    3. 抽象化(组件框架)

四:高阶函数

  • 以函数作为参数
  • 以函数作为返回值
  • 常用于作为函数装饰器

1.常用高阶函数

Once

Throttle

Debounce

Consumer/2

Iterative

2.为什么要用高阶函数

使用高阶函数能大大减少我们使用非纯函数的可能性
而非纯函数越多,可维护性越差

:五:当年的Leftpad事件

 function leftpad(str, len, ch) {
      str = String(str);
      var i = -1;
      if (!ch && ch !== 0) ch = ' ';
      len = len - str.length;
      while (++i < len) {
          str = ch + str;
      }
      return str;
  } 

事件本身的槽点:

  • NPM 模块粒度
  • 代码风格
  • 代码质量/效率

改进后代码

function leftpad(str, len, ch) {
      str = "" + str;
      const padLen = len - str.length;
      if(padLen <= 0) {
        return str;
      }
      return (""+ch).repeat(padLen)+str;
  } 
  • 代码更简洁
  • 效率提升