JavaScript 编码原则之组件封装
组件是指Web页面上抽出来一个包含模版(HTML) 、功能(JS)和样式(CSS)的单元。好的组件备封装性、正确性、扩展性、复用性。
结构:HTML
轮播图是个无序列表结构,可以使用<ul>实现
表现:CSS
- 使用 CSS 绝对定位将图片重叠在同一个位置
- 轮播图切换的状态使用修饰符(modifier)
- 轮播图的切换动画使用 CSS transition
行为:API
定义的一些行为的接口
Slider
- +getSelectedltem( )
- +getSelectedltemIndex()
- +slideTo()
- +slideNext()
- +slidePrevious()
这段代码定义了一个 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()方法,实现了轮播图的自动切换功能。
行为:控制流
现在加上左右滑动的标记还有下面的小圆点
这段代码扩展了之前的 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方法停止自动切换,然后调用slidePrevious或slideNext方法切换到上一项或下一项,最后调用start方法重新开始自动切换。 - 在
Slider类中添加了start方法和stop方法。start方法首先调用stop方法停止之前的定时器,然后使用setInterval函数创建一个新的定时器,定时调用slideNext方法切换到下一项。定时器的时间间隔由构造函数中的cycle参数指定。stop方法则清除之前创建的定时器。 - 最后,创建了
Slider类的实例slider,并调用start方法开始自动切换。
通过这些扩展,轮播图现在具有了自动切换、鼠标悬停暂停、控制按钮切换等功能。
总结:基本方法
-
结构设计
-
展现效果
-
行为设计
- API (功能)
- Event (控制流)
重构:插件化
将控制元素抽取成插件,插件与组件之间通过依赖注入方式建立联系
这段代码再对之前的 Slider 类进行了扩展,引入了插件机制。
- 在
Slider类中,添加了一个registerPlugins方法,接受多个插件作为参数。在方法内部,使用forEach方法遍历插件数组,并将当前的Slider实例作为参数调用每个插件函数。这样,每个插件函数都可以访问到Slider实例,并在其中添加特定的功能。 - 接下来,定义了三个插件函数:
pluginController、pluginPrevious和pluginNext。这些插件函数接受一个slider参数,表示当前的Slider实例。 pluginController插件函数用于处理轮播图的控制器功能。在函数内部,获取控制器元素和按钮元素,并添加事件监听器。事件监听器的逻辑与之前的代码相同,用于处理鼠标悬停、鼠标离开和轮播图切换时的样式变化。pluginPrevious插件函数用于处理轮播图的“上一项”按钮功能。在函数内部,获取“上一项”按钮元素,并添加点击事件监听器。事件监听器的逻辑与之前的代码相同,用于停止自动切换、切换到上一项、重新开始自动切换。pluginNext插件函数用于处理轮播图的“下一项”按钮功能。在函数内部,获取“下一项”按钮元素,并添加点击事件监听器。事件监听器的逻辑与之前的代码相同,用于停止自动切换、切换到下一项、重新开始自动切换。- 最后,创建了
Slider类的实例slider,并调用registerPlugins方法注册了三个插件:pluginController、pluginPrevious和pluginNext。这样,这些插件的功能就会被添加到Slider实例中。最后调用start方法开始自动切换。
通过这种插件机制,可以方便地扩展 Slider 类的功能,添加自定义的插件,实现更多的交互效果和样式变化。
重构:模板化
将HTML模板化,更易于拓展
这段代码是对之前的 Slider 类进行了一些改进和扩展,将HTML模板化。
- 在
Slider类的构造函数中,接受一个名为opts的参数,用于传递选项对象。选项对象中包含了images和cycle两个属性,默认值分别为一个空数组和 3000。这样,创建Slider实例时可以传入自定义的图片和切换周期。 - 在构造函数中,调用
render方法生成轮播图的 HTML 内容,并将其赋值给轮播图容器元素的innerHTML属性。render方法根据选项对象中的images属性生成轮播图的 HTML 结构。 - 在
registerPlugins方法中,对每个插件进行处理的逻辑有所改变。现在,每个插件都是一个对象,包含了两个方法:render和action。render方法用于生成插件的 HTML 内容,而action方法用于添加插件的功能。 - 在
registerPlugins方法中,首先创建一个插件容器元素,并将其添加到轮播图容器中。然后,分别调用每个插件的render方法生成插件的 HTML 内容,并将其赋值给插件容器元素的innerHTML属性。最后,调用每个插件的action方法,将当前的Slider实例作为参数传递给插件函数,以添加插件的功能。 - 插件函数
pluginController、pluginPrevious和pluginNext的逻辑与之前的代码相同,只是现在它们被定义为对象的方法。 - 在
pluginController的render方法中,根据传入的图片数组生成控制器按钮的 HTML 内容。 - 在
pluginController的action方法中,获取控制器元素和按钮元素,并添加事件监听器,逻辑与之前的代码相同。 - 在
pluginPrevious和pluginNext的render方法中,生成“上一项”和“下一项”按钮的 HTML 内容。 - 在
pluginPrevious和pluginNext的action方法中,获取按钮元素,并添加点击事件监听器,逻辑与之前的代码相同。 - 最后,创建了
Slider类的实例slider,并传入了选项对象,包含了自定义的图片和切换周期。然后,调用registerPlugins方法注册了三个插件:pluginController、pluginPrevious和pluginNext。最后调用start方法开始自动切换。
通过这些改进和新增的功能,现在可以更灵活地配置轮播图,自定义图片和插件,并实现更多的交互效果。
重构:组件框架
将组件通用模型抽象出来
这段代码对之前的代码进行了一些改进。
- 现在,
Slider类继承自Component类,而不是直接定义。这样做的好处是可以将共享的逻辑放在Component类中,而不是在每个子类中重复编写。 - 在
Component类的构造函数中,接受一个名为opts的参数,其中包含了name和data两个属性。name属性用于设置插件容器的类名,data属性用于传递数据。在构造函数中,调用render方法生成组件的 HTML 内容,并将其赋值给组件容器元素的innerHTML属性。 registerPlugins方法的逻辑与之前基本相同,只是在创建插件容器元素的类名中使用了name属性。Slider类的构造函数中,通过调用super方法来调用父类Component的构造函数,并传递id和opts参数。然后,继续进行子类特定的逻辑。- 在
Slider类中,重写了父类的render方法,用于生成轮播图的 HTML 内容。这里的render方法接受一个名为data的参数,代表轮播图的数据。逻辑与之前的代码相同。 - 其他方法和事件处理逻辑与之前的代码相同。
- 最后,创建了
Slider类的实例slider,并传入了选项对象,包含了自定义的name、data和cycle。然后,调用registerPlugins方法注册了三个插件:pluginController、pluginPrevious和pluginNext。最后调用start方法开始自动切换。
通过这种继承的方式,可以更好地组织代码,减少重复编写,并且方便扩展和修改。
总结
-
组件设计的原则:封装性、正确性、扩展性、复用性
-
实现组件的步骤:结构设计、展现效果、行为设计
-
三次重构
- 插件化
- 模板化
- 抽象化(组件框架)