如何写好JavaScript 1| 青训营笔记

68 阅读3分钟

image.png

这是我参与「第四届青训营 」笔记创作活动的的第2天

写好JS的一些原则

  1. 各司其职 —— 让html、css和JavaScript职能分离

  2. 组件封装 —— 正确性 拓展性 复用性

  3. 过程抽象 —— 应用函数式编程思想

一. 各司其职

实例 浅色和深色两个浏览模式

//版本一
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;
}

#### 总结

  1. 应当避免不必要的由JS直接操作样式

  2. 可以用class来表示状态

  3. 纯展示类交互寻求零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 ''
	}
}

### 总结

  • 组件设计的原则:封装性、正确性、扩展性、复用性
  • 实现组件的步骤:结构设计、展现效果、行为设计
  • 三次重构 —— 插件化 模板化 抽象化(组件框架)

注:以上代码均为上课笔记