javascript中组件的插件化-模块化-抽象化

javascript

一个简单案例

切换文章的夜间与日间模式

初始 js 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
const btn = document.querySelector("#btn");
btn.addEventListener("click", (e) => {
const body = document.body;
if (e.target.innerHTML === "☀") {
body.style.backgroundColor = "black";
body.style.color = "white";
e.tartget.innerHTML = "🌙";
} else {
body.style.backgroundColor = "white";
body.style.color = "black";
e.tartget.innerHTML = "☀";
}
});

可见 js 直接操控了样式,但代码不能复用且冗余,其他人读起来语义不明,修改不便,有很高的修改空间。

修改后:

1
2
3
4
5
6
7
8
9
const btn = document.querySelector("#btn");
btn.addEventListener("click", (e) => {
const body = document.body;
if (body.className !== "night") {
body.className = "night";
} else {
body.className = "";
}
});

修改后,js 仅操控了 body 的 class,而没有接触到 css,若有修改的需求可以直接修改 css,而不是修改 js。

再次修改后:

1
2
3
4
5
6
7
8
/*css*/
#modeCheckBox {
display: none;
}
#modeCheckBox:checked + .content {
background-color: black;
color: white;
}
1
2
3
4
<input id="modeCheckBox" type="checkbox" />
<div class="content">
<label id="modeBtn" for="modeCheckBox"></label>
</div>

再次修改后,成为了纯 css 代码,不需要 js 的参与,这样就将样式与 js 彻底分隔开,避免了不必要的 js 操控样式,做到了 HTML/CSS/JS 各司其职。

组件封装

轮播图:

HTML

轮播图是典型列表结构,所以可以用无序列表来实现

CSS

每个 li 标签中插入一张图片,然后利用 css 的绝对定位,使图片重合在一起。

其中,未被选择的图片 opacity 置为 0,使之透明不可见。已选择的图片则将 opacity 置为 1;

1
2
3
4
5
6
7
8
9
10
11
12
.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;
}

由于已选择元素与被选择元素有一些相似性,所以共同设置样式,又由于有不同点,故要将被选择的元素单独添加样式。

JS

在 JS 中,单独定义了一个 Slider 类用于控制样式的改变

Slider 类中有一个构造方法,用于将所有的 li 元素保存进 items。参数为一个 id,代表轮播图列表。

1
2
3
4
this.container = document.getElementById(id);
this.items = this.container.querySelectorAll(
".slider-list__item, .slider-list__item--selected"
);

getSelectedItem 方法

用于获取已经被选择的元素,返回值是一个元素。

1
2
const selected = this.container.querySelector(".slider-list__item--selected");
return selected;

getSelectedItemIndex 方法

用于获取已经被选择的元素在 items 中的位置。

1
return Array.from(this.items).indexOf(this.getSelectedItem());

slideTo 方法

用于选择指定次序的图片,需要一个参数 idx,代表图片在 items 中的位置。将已选择的图片取消选择,将传入的 items[idx]的图片选择。

1
2
3
4
5
6
7
8
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 方法

用于轮播图滑动向下一张图片

1
2
3
const currentIdx = this.getSelectedItemIndex();
const nextIdx = (currentIdx + 1) % this.items.length;
this.slideTo(nextIdx);

为防止 nextIdx 过大,故采用取余的方式。

slidePrevious 方法

用于轮播图滑动向上一张图片

1
2
3
const currentIdx = this.getSelectedItemIndex();
const previousIdx = (this.items.length + currentIdx - 1) % this.items.length;
this.slideTo(previousIdx);

为防止 previousIdx 产生负数或 0,故加上了 items 的长度并取余。

这种方式灵活性较差,仍有优化空间。

插件化

将轮播图中的左右键与下方原点以插件的形式插入轮播图,方便了轮播图的扩展和维护。但删除插件后,控件仍保留在 HTML 中,没有实际作用。

插件化轮播图

模板化

模板化让轮播图能动态的生成 HTML 模板,添加图片时仅须修改 js 代码,而不必改动 HTML,将 JS 与 HTML 的依赖分开。当不需要某插件时,可以直接删除插件,而不用改动 html,灵活性高。

模板化轮播图

组件框架

实现的组件框架将组件通用模型抽象出来,利用继承来实现轮播图,能完成多种不同样式的轮播图,进一步提高了灵活性,复用性。

组件化轮播图

总结

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

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

  • 三次重构:

    • 插件化
    • 模板化
    • 抽象化