这是我参与「第五届青训营 」伴学笔记创作活动的第 三 天
如何写好js
写好js的一些原则
一.各司其责
在web开发中,html负责网页的结构,css负责网页上各元素的展示样式,js负责网页和用户的交互。想要成为一名优秀的前端工程师,首先要做的就是遵守这三者各司其职的原则,让我们的代码易于维护和扩展。
例子:
写一段js代码,控制一个网页,让它支持浅色和深色两种模式,你会怎么做?
版本一:通过JS直接修改元素样式
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 = '🌞';
}
});
版本二:用CSS的class表示状态,用JS切换
const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e) => {
const body = document.body;
if(body.className !== 'night') {
body.className = 'night';
} else {
body.className = '';
}
});
版本三:纯展现类交互可以只用HTML+CSS实现
<body>
<input id="modeCheckBox" type="checkbox">
<div class="content">
<header>
<label id="modeBtn" for="modeCheckBox"> label>
<h1>深夜食堂</h1>
</header>
<main>
...
</main>
</div>
</body>
#modeCheckBox:checked + .content {
backgroundcolor: black;
color: white;
transition: all 1s;
}
这个例子告诉我们,在实际开发中要注意:
- HTML/CSS/JS要各司其责
- 应当避免不必要的由js直接控制样式
- 可以用class来表示状态
- 纯交互类展示寻求零js方案
二.组件封装
组件是指Web页面上抽出来一个个包含模版(HTML),功能(js)和样式(css)的单元,好的组件具备封装性,正确性,拓展性,复用性。
例子:
用原生js 写一个电商网站的轮播图,应该怎么实现。
结构设计:HTML
首先设计HTML结构。轮播图是一个典型的列表结构,我们可以使用无序列表
<div id="my-slider" class="slider-list">
<ul>
<li class="slider-list_item--selected">
<img src="https://p5.sst.qhing.com/t0119c746247889dd070.png" />
</li>
<li class="slider-list_item">
<img src="https://p5.sst.qhing.com/t011adc46247889dd070.png" />
</li>
<li class="slider-list_item">
<img src="https://p5.sst.qhing.com/t011adc462475b84d070.png" />
</li>
<li class="slider-list_item">
<img src="https://p5.sst.qhing.com/t011adc46247889dd578.png" />
</li>
</ul>
</div>
结构设计:CSS
- 使用css绝对定位将图片重叠在同一位置 轮播图切换的状态使用修饰符(modifier) 轮播图的切换动画使用 css transition
#my-slider{
position: relative;
width: 790px;
}
.slider-list ul{
list-style-type: none;
position: relative;
padding: 0;
margin: 0;
}
.slider-list_item,.slider-list_item--selected{
position: absolute;
transition: opacity ls;
opacity: 0;
text-align: center;
}
slider-list_item--selected{
transition: opacity ls;
opacity: 1;
}
API设计
API设计应保证原子操作,职责单一,满足灵活性。
getSeletedItem() 可以将当前轮播到的图片元素获取到 getSelectedItemIndex() 当前播放到几张图了,获得下标 slideTo() 指定播放到某一张图片的1位置去 slideNext() 结合slideTo() slidePrevious() 结合slideTo()
class Slider{
constructor(id){
this.container = document.getElementById(id);
this.items = this.container
.querySelectorAll('.sliderlist item, .sliderlist item selected');
}
getSelectedItem(){
const selected = this.container
.querySelector('.sliderlist item selected');
return selected
}
getSelectedItemIndex(){
return Array.from(this.items).indexOf(this.getSelectedItem());
}
slideTo(idx){
const selected = this.getSelectedItem();
if(selected){
selected.className = 'sliderlist item';
}
const item = this.items[idx];
if(item){
item.className = 'sliderlist 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.item.length + currentIdx - 1) % this.item.length;
this.slideTo(previousIdx);
}
}
控制流设计
设置控制结构,响应用户操作。可以使用自定义事件来解耦行为。
<a class="slide-list__next"></a>
<a class="slide-list__previous"></a>
<div class="slide-list__control">
<span class="slide-list__control-buttons--selected"></span>
<span class="slide-list__control-buttons"></span>
<span class="slide-list__control-buttons"></span>
<span class="slide-list__control-buttons"></span>
</div>
</div>
总结:
基本方法
-
结构设计
-
展现效果
-
行为设计
API(功能)
Event(控制流)
改进轮播图
优化1:插件化:
将控制元素抽取成插件,插件与组件之间通过依赖注入方式建立联系
优化2:模板化:
class Slider{
constructor(id, opts = {images:[], cycle: 3000}){
...
}
render(){
const images = this.options.images;
const content = images.map(image=>`
<li class="sliderlist_item">
<img src="${image}" />
</li>
`.trim());
return `<ul>${content.join('')}</ul>`;
}
...
}
优化3:组件模型:
总结:
组件设计的原则:封装性,正确性,拓展性,复用性。
实现组件的步骤:结构设计,展现效果,行为设计
三次重构
插件化
模板化
抽象化(组件框架)
三.过程抽象
(1)用来处理局部细节控制的一些方法。 (2)函数式编程思想的基础应用。
控制代码执行次数:
限制某个方法只能执行一次。 (1)一些异步交互 (2)一次性的HTTP请求
once:
为了能够让“只执行一次”的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为过程抽象。
高阶函数:
(1)以函数作为参数 (2)以函数作为返回值 (3)常用于作为函数装饰器
一些高阶函数
四.命令式与声明式:
let list = [1, 2, 3, 4];
let map1 = [];
for(let i = 0; i < list.length; i++ ){
map1.push(list[i] * 2);
}
let list = [1, 2, 3, 4];
const double = x=>x * 2;
list.map(double);