这是我参与「第四届青训营 」笔记创作活动的的第2天
写好JS的一些原则
-
各司其职 —— 让html、css和JavaScript职能分离
-
组件封装 —— 正确性 拓展性 复用性
-
过程抽象 —— 应用函数式编程思想
一. 各司其职
实例 浅色和深色两个浏览模式
//版本一
const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e) => {
const body = document.body;
if(e.target.innerHTML === '🌞') {
body.style.backgroundColor = 'black';
body.style.color = 'white';
e.target.innerHTML = '🌜';
} else {
body.style.backgroundColor = 'white';
body.style.color = 'black';
e.target.innerHTML = '🌞';
}
});
对于版本一,如果改动样式需求,比如我不要这个背景颜色,那就会在js中修改样式,所以未实现职能分离。
//版本二
const btn = document.getElementById('modeBtn');
btn.addEventListener('click',(e)=>{
const body=document.body;
if(body.className!=='night'){
body.className='night';
}else{
body.className='';
}
})
对于版本二,实现了职能分离,但这段js就是用来控制样式的,所以最好是用纯css实现。
//版本三——纯css实现
<body>
<input id="modeCheckBox" type="checkbox">
<div class="content">
<header>
<label id="modeBtn" for="modeCheckBox"></label>
<h1>深夜食堂</h1>
</header>
<main>
<div class="pic">
<img src="https://p2.ssl.qhimg.com/t0120cc20854dc91c1e.jpg">
</div>
<div class="description">
<p>
这是一间营业时间从午夜十二点到早上七点的特殊食堂。
</p>
</div>
</main>
</div>
</body>
#modeCheckBox {
display: none;
}
#modeCheckBox:checked + .content {
background-color: black;
color: white;
transition: all 1s;
}
#### 总结
-
应当避免不必要的由JS直接操作样式
-
可以用class来表示状态
-
纯展示类交互寻求零JS方案
二. 组件封装
组件是指Web页面上抽出来一个个包含模版(HTML)、功能(JS)和样式(CSS)的单元。
实例 轮播图
轮播图是一个典型的列表结构
//样式部分
#my-slider{
position: relative;
width: 790px;
height: 340px;
}
.slider-list ul{
list-style-type:none;
position: relative;
width: 100%;
height: 100%;
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;
}
- 使用 CSS 绝对定位将图片重叠在同一个位置
- 轮播图切换的状态使用修饰符 —— 是用于限定类型以及类型成员的声明的一种符号
- 轮播图的切换动画使用 CSS transition
//API功能实现
class Slider{
constructor(id, cycle = 3000){
this.container = document.getElementById(id);
this.items = this.container.querySelectorAll('.slider-list__item, .slider-list__item--selected');
this.cycle = cycle;
}
getSelectedItem(){
const selected = this.container
.querySelector('.slider-list__item--selected');
return selected
}
getSelectedItemIndex(){
return Array.from(this.items).indexOf(this.getSelectedItem());
}
slideTo(idx){
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(){
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);
}
start(){
this.stop();
this._timer = setInterval(()=>this.slideNext(), this.cycle);
}
stop(){
clearInterval(this._timer);
}
}
const slider = new Slider('my-slider');
slider.start();
slider= getSelectedItem+getSelectedItemIndex+slideTo+slideNext+ slidePrevious
接下来要加入轮播图的左右键以及导航点
//js
const detail = {index: idx}
const event = new CustomEvent('slide', {bubbles:true, detail})
this.container.dispatchEvent(event)
行为:控制流——按一定的顺序排列程序元素来决定程序执行的顺序
使用自定义事件来解耦
解耦就是用数学方法将两种运动分离开来处理问题
#### 总结重点:
- 结构设计
- 展现效果
- 行为设计 —— API (功能) Event(控制流)
### 优化——解耦
重构——插件化
- 将控制元素抽取成插件
- 插件与组件之间通过依赖注入方式建立联系
重构——模板化
新增插件
将HTML模板化,更易于扩展,增减功能在js中就能一次搞定,下图蓝色位置增添修改
组件框架——抽象化
将组件通用模型抽象出来
class Component{
constructor(id, opts = {name, data:[]}){
this.container = document.getElementById(id);
this.options = opts;
this.container.innerHTML = this.render(opts.data);
}
registerPlugins(...plugins){
plugins.forEach(plugin => {
const pluginContainer = document.createElement('div');
pluginContainer.className = `.${name}__plugin`;
pluginContainer.innerHTML = plugin.render(this.options.data);
this.container.appendChild(pluginContainer);
plugin.action(this);
});
}
render(data) {
/* abstract */
return ''
}
}
### 总结
- 组件设计的原则:封装性、正确性、扩展性、复用性
- 实现组件的步骤:结构设计、展现效果、行为设计
- 三次重构 —— 插件化 模板化 抽象化(组件框架)
注:以上代码均为上课笔记