JavaScript 编码原则之组件封装

154 阅读10分钟

JavaScript 编码原则之组件封装

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

结构:HTML

轮播图是个无序列表结构,可以使用<ul>实现

表现:CSS

  • 使用 CSS 绝对定位将图片重叠在同一个位置
  • 轮播图切换的状态使用修饰符(modifier)
  • 轮播图的切换动画使用 CSS transition

行为:API

定义的一些行为的接口

Slider

  • +getSelectedltem( )
  • +getSelectedltemIndex()
  • +slideTo()
  • +slideNext()
  • +slidePrevious()

轮播图(withAPI)

这段代码定义了一个 Slider 类,用于管理轮播图的功能。它包括了一些方法来获取选中项、切换到指定索引的项以及自动切换到下一项和上一项。

  • 在构造函数中,传入的 id 参数用于获取轮播图的容器元素,并通过 querySelectorAll 方法获取所有的轮播项(.slider-list__item.slider-list__item--selected)。
  • getSelectedItem 方法用于获取当前选中的轮播项,它通过 querySelector 方法选择具有 .slider-list__item--selected 类的元素。
  • getSelectedItemIndex 方法用于获取当前选中项的索引,它使用 Array.from 方法将轮播项转换为数组,并使用 indexOf 方法找到选中项在数组中的索引。
  • slideTo 方法用于将轮播图切换到指定索引的项。它首先将当前选中项的类名设置为 'slider-list__item',然后将指定索引的项的类名设置为 'slider-list__item--selected'
  • slideNext 方法用于切换到下一项。它通过调用 getSelectedItemIndex 方法获取当前选中项的索引,然后计算下一项的索引。使用取模运算符 % 来确保索引在有效范围内,避免超出数组长度。
  • slidePrevious 方法用于切换到上一项。它与 slideNext 方法类似,不同之处在于计算上一项的索引时使用了特定的计算方式,确保索引在有效范围内。
  • 最后创建 Slider 类的实例 slider,并使用 setInterval 函数每隔 2 秒调用 slider.slideNext() 方法,实现了轮播图的自动切换功能。

行为:控制流

现在加上左右滑动的标记还有下面的小圆点

轮播图(with控制流)

这段代码扩展了之前的 Slider 类,添加了一些额外的功能。

  • Slider 类的构造函数中,添加了一个可选参数 cycle,用于指定轮播图自动切换的时间间隔,默认为 3000 毫秒(即 3 秒)。这个参数用于设置 start 方法中定时器的时间间隔。
  • 接下来,代码通过 querySelector 方法获取轮播图控制器元素(.slide-list__control),并检查是否存在。如果存在,则获取控制器中的按钮元素(.slide-list__control-buttons.slide-list__control-buttons--selected)。
  • 在控制器元素上添加了 mouseover 事件监听器,当鼠标悬停在按钮上时,会触发回调函数。回调函数中,通过 Array.from 方法找到鼠标悬停的按钮在按钮数组中的索引,并调用 slideTo 方法切换到对应的项。同时,调用 stop 方法停止自动切换。
  • 在控制器元素上添加了 mouseout 事件监听器,当鼠标离开控制器时,会触发回调函数。回调函数中,调用 start 方法重新开始自动切换。
  • 在轮播图容器元素上添加了 slide 自定义事件监听器。当轮播图切换到新的项时,会触发该事件,并将切换后的项的索引作为事件的详细信息传递。回调函数中,根据索引找到对应的按钮元素,并修改其类名以反映当前选中项。
  • 接下来,代码获取了轮播图中的“上一项”和“下一项”按钮元素,并分别添加了点击事件监听器。在点击事件的回调函数中,先调用 stop 方法停止自动切换,然后调用 slidePreviousslideNext 方法切换到上一项或下一项,最后调用 start 方法重新开始自动切换。
  • Slider 类中添加了 start 方法和 stop 方法。start 方法首先调用 stop 方法停止之前的定时器,然后使用 setInterval 函数创建一个新的定时器,定时调用 slideNext 方法切换到下一项。定时器的时间间隔由构造函数中的 cycle 参数指定。stop 方法则清除之前创建的定时器。
  • 最后,创建了 Slider 类的实例 slider,并调用 start 方法开始自动切换。

通过这些扩展,轮播图现在具有了自动切换、鼠标悬停暂停、控制按钮切换等功能。

总结:基本方法

  • 结构设计

  • 展现效果

  • 行为设计

    1. API (功能)
    2. Event (控制流)

重构:插件化

将控制元素抽取成插件,插件与组件之间通过依赖注入方式建立联系

轮播图(with插件化)

这段代码再对之前的 Slider 类进行了扩展,引入了插件机制。

  • Slider 类中,添加了一个 registerPlugins 方法,接受多个插件作为参数。在方法内部,使用 forEach 方法遍历插件数组,并将当前的 Slider 实例作为参数调用每个插件函数。这样,每个插件函数都可以访问到 Slider 实例,并在其中添加特定的功能。
  • 接下来,定义了三个插件函数:pluginControllerpluginPreviouspluginNext。这些插件函数接受一个 slider 参数,表示当前的 Slider 实例。
  • pluginController 插件函数用于处理轮播图的控制器功能。在函数内部,获取控制器元素和按钮元素,并添加事件监听器。事件监听器的逻辑与之前的代码相同,用于处理鼠标悬停、鼠标离开和轮播图切换时的样式变化。
  • pluginPrevious 插件函数用于处理轮播图的“上一项”按钮功能。在函数内部,获取“上一项”按钮元素,并添加点击事件监听器。事件监听器的逻辑与之前的代码相同,用于停止自动切换、切换到上一项、重新开始自动切换。
  • pluginNext 插件函数用于处理轮播图的“下一项”按钮功能。在函数内部,获取“下一项”按钮元素,并添加点击事件监听器。事件监听器的逻辑与之前的代码相同,用于停止自动切换、切换到下一项、重新开始自动切换。
  • 最后,创建了 Slider 类的实例 slider,并调用 registerPlugins 方法注册了三个插件:pluginControllerpluginPreviouspluginNext。这样,这些插件的功能就会被添加到 Slider 实例中。最后调用 start 方法开始自动切换。

通过这种插件机制,可以方便地扩展 Slider 类的功能,添加自定义的插件,实现更多的交互效果和样式变化。

重构:模板化

将HTML模板化,更易于拓展

image-20230731210809081

轮播图(with模块化)

这段代码是对之前的 Slider 类进行了一些改进和扩展,将HTML模板化。

  • Slider 类的构造函数中,接受一个名为 opts 的参数,用于传递选项对象。选项对象中包含了 imagescycle 两个属性,默认值分别为一个空数组和 3000。这样,创建 Slider 实例时可以传入自定义的图片和切换周期。
  • 在构造函数中,调用 render 方法生成轮播图的 HTML 内容,并将其赋值给轮播图容器元素的 innerHTML 属性。render 方法根据选项对象中的 images 属性生成轮播图的 HTML 结构。
  • registerPlugins 方法中,对每个插件进行处理的逻辑有所改变。现在,每个插件都是一个对象,包含了两个方法:renderactionrender 方法用于生成插件的 HTML 内容,而 action 方法用于添加插件的功能。
  • registerPlugins 方法中,首先创建一个插件容器元素,并将其添加到轮播图容器中。然后,分别调用每个插件的 render 方法生成插件的 HTML 内容,并将其赋值给插件容器元素的 innerHTML 属性。最后,调用每个插件的 action 方法,将当前的 Slider 实例作为参数传递给插件函数,以添加插件的功能。
  • 插件函数 pluginControllerpluginPreviouspluginNext 的逻辑与之前的代码相同,只是现在它们被定义为对象的方法。
  • pluginControllerrender 方法中,根据传入的图片数组生成控制器按钮的 HTML 内容。
  • pluginControlleraction 方法中,获取控制器元素和按钮元素,并添加事件监听器,逻辑与之前的代码相同。
  • pluginPreviouspluginNextrender 方法中,生成“上一项”和“下一项”按钮的 HTML 内容。
  • pluginPreviouspluginNextaction 方法中,获取按钮元素,并添加点击事件监听器,逻辑与之前的代码相同。
  • 最后,创建了 Slider 类的实例 slider,并传入了选项对象,包含了自定义的图片和切换周期。然后,调用 registerPlugins 方法注册了三个插件:pluginControllerpluginPreviouspluginNext。最后调用 start 方法开始自动切换。

通过这些改进和新增的功能,现在可以更灵活地配置轮播图,自定义图片和插件,并实现更多的交互效果。

重构:组件框架

将组件通用模型抽象出来

image-20230731210742918

轮播图(with模块化)

这段代码对之前的代码进行了一些改进。

  • 现在,Slider 类继承自 Component 类,而不是直接定义。这样做的好处是可以将共享的逻辑放在 Component 类中,而不是在每个子类中重复编写。
  • Component 类的构造函数中,接受一个名为 opts 的参数,其中包含了 namedata 两个属性。name 属性用于设置插件容器的类名,data 属性用于传递数据。在构造函数中,调用 render 方法生成组件的 HTML 内容,并将其赋值给组件容器元素的 innerHTML 属性。
  • registerPlugins 方法的逻辑与之前基本相同,只是在创建插件容器元素的类名中使用了 name 属性。
  • Slider 类的构造函数中,通过调用 super 方法来调用父类 Component 的构造函数,并传递 idopts 参数。然后,继续进行子类特定的逻辑。
  • Slider 类中,重写了父类的 render 方法,用于生成轮播图的 HTML 内容。这里的 render 方法接受一个名为 data 的参数,代表轮播图的数据。逻辑与之前的代码相同。
  • 其他方法和事件处理逻辑与之前的代码相同。
  • 最后,创建了 Slider 类的实例 slider,并传入了选项对象,包含了自定义的 namedatacycle。然后,调用 registerPlugins 方法注册了三个插件:pluginControllerpluginPreviouspluginNext。最后调用 start 方法开始自动切换。

通过这种继承的方式,可以更好地组织代码,减少重复编写,并且方便扩展和修改。

总结

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

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

  • 三次重构

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