JavaScript 编码原则之组件封装 | 青训营笔记

59 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 9 天,本节课👩🏻‍🏫通过一个轮播图例子,让我们知道了组件封装的过程,主要包括结构设计,这个是最重要的,先把结构设计好,然后是展现效果模拟,最后是行为设计包括一些api和event事件

组件封装

组件定义:指web页面上抽出来一个个包含模版(html)、功能(js)和样式(css)的单元,好的组件具备封装性、正确性、扩展性、复用性。

例子:用原生js写一个电商网站的轮播图

结构设计: HTML

轮播图是一个典型的列表结构,我们可以使用无序列表<ul>元素来实现

 <div id="app" class="app-list">
    <ul>
      <li class="app-list__item-selected">
        <img src="https://images.unsplash.com/photo-1617709612173-d820eb8b862f?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHx0b3BpYy1mZWVkfDJ8NnNNVmpUTFNrZVF8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=80">
      </li>
      <li class="app-list__item">
        <img src="https://images.unsplash.com/photo-1666705040497-1890be6e357c?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=80">
      </li>
      <li class="app-list__item">
        <img src="https://images.unsplash.com/photo-1674318012388-141651b08a51?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=80">
      </li>
      <li class="app-list__item">
        <img src="https://images.unsplash.com/photo-1674240568812-d7481f3699a7?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=80">
      </li>
    </ul>
  </div>

表现: CSS

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

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

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

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

行为:JS

  • API设计应保证原子操作,职责单一,满足灵活性
  • Slider
    • getSelectedItem()
    • getSelectedItemIndex()
    • slideTo()
    • slideNext()
    • slidePrevious()
   class app {
      constructor(id) {
        this.container = document. getElementById(id);
        this.items = this.container
          .querySelectorAll('.app-list__item,.app-list__item--selected');
      }
      
      getSelectedItem() {
        const selected = this.container
          querySelector('.app-list__item--selected');
        return selected
      }
      
      getSelectedItemIndex() {
        return Array.from(this. items).indexof(this.getSelectedItem());
      }

      slideTo(idx) {
        const selected = this.getSelectedItem();
        if(selected){
          selected.className = "app-list__item";
        }
        const item = this.items[idx];
        if(item) {
          item.className = 'app-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);
      }
    }

行为:控制流

  • 使用自定义事件来解耦
<a class="app-listnext"></a>
<a class="app-list__previous">s/a>
<div class="app-list__control">
	<span class="app-list__control-buttons--selected"></span>
	<span class="app-list__control-buttons"></span>
	<span class="app-list__control-buttons"></span>
	<span class="app-list__control-buttons"></span>
</div>
const detail = {index: idx}
const event = new CustomEvent('slide',{bubbles:true, detail})
this.container.dispatchEvent(event)

总结:基本方法

  • 结构设计

  • 展现效果

  • 行为设计

    • Api(功能)

    • Event(控制流)

解耦

  • 将控制元素抽取成插件

  • 插件与组件之间通过依赖注入方式建立联系

  • html模板化

抽象:

  • 将通用的组件模型抽象出来