前端之js进阶|青训营笔记

49 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 三 天

如何写好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:插件化:

将控制元素抽取成插件,插件与组件之间通过依赖注入方式建立联系

img

优化2:模板化:

image-20230120110738348

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)函数式编程思想的基础应用。

img

控制代码执行次数:

限制某个方法只能执行一次。 (1)一些异步交互 (2)一次性的HTTP请求

once:

为了能够让“只执行一次”的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为过程抽象。

高阶函数:

(1)以函数作为参数 (2)以函数作为返回值 (3)常用于作为函数装饰器

一些高阶函数
  1. once
  2. throttle
  3. debounced
  4. consumer
  5. iterative

四.命令式与声明式:

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);