各司其职
- HTML/CSS/JS各司其职
- 应当避免不必要的由JS直接操作样式
- 可以用class来表示状态
- 纯展示类交互寻求零JS方案(可以用CSS实现)
例如:实现一个网页的“夜间模式”
方法一:
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直接操作样式
- 代码显得复杂繁琐、且复用性差
改进:
- 可以用class来表示状态
方法二:
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实现此效果,让代码更简洁
方法三:(推荐)
<input id="modeCheckBox" type="checkbox">
<div class="content">
<header>
<label id="modeBtn" for="modeCheckBox"></label>
<h1>深夜食堂</h1>
</header>
<main>
······
</main>
</div>
复制代码
#modeCheckBox:checked + .content {
background-color: black;
color: white;
transition: all 1s;
}
复制代码
优点:
- 是非常好的零js展示方案
- 完全满足了HTML/CSS/JS各司其职的书写理念
组件封装
组件:指Web页面上抽出来的一个个包含模板(HTML)、功能(JS)、样式(CSS)的单元
组件设计的原则:封装性、正确性、扩展性、复用性
实现组件的步骤:
如轮播图为例:轮播图
结构设计:HTML
轮播图是一个典型的列表结构,可以使用无序列表
- 元素来实现
<div id="my-slider" class="slider-list">
<ul>
<li class="slider-list__item--selected">
<img src="https://p5.ssl.qhimg.com/t0119c74624763dd070.png"/>
</li>
<li class="slider-list__item">
<img src="https://p4.ssl.qhimg.com/t01adbe3351db853eb3.jpg"/>
</li>
<li class="slider-list__item">
<img src="https://p2.ssl.qhimg.com/t01645cd5ba0c3b60cb.jpg"/>
</li>
<li class="slider-list__item">
<img src="https://p4.ssl.qhimg.com/t01331ac159b58f5478.jpg"/>
</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 1s;
opacity: 0;
text-align: center;
}
.slider-list__item--selected{
transition: opacity 1s;
opacity: 1;
}
复制代码
行为设计
API(功能)
API设计应保证原子操作,职责单一,满足灵活性。
-
Slider
- +getSelectedItem( ) 当前选中被展现出的图
- +getSelectedItemIndex( ) 当前选中被展现出的图的索引
- +slideTo( ) 跳转并展示某个图
- +slideNext( ) 切换下一张图
- +slidePrevious( ) 切换上一张图
class Slider{
constructor(id){
this.container = document.getElementById(id);
this.items = this.container
.querySelectorAll('.slider-list__item, .slider-list__item--selected');
}
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);
}
}
//展示第一张图
const slider = new Slider('my-slider');
slider.slideTo(1);
复制代码
Event(控制流)
轮播图下面的控制按钮
<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>
复制代码
使用自定义事件来解耦
const detail = {index: idx}
const event = new CustomEvent('slide', {bubbles:true, detail})
this.container.dispatchEvent(event)
复制代码
组件封装优点:
- 复用性强
- 可读性好
- 加强自己的代码管理能力
过程抽象
- 用来处理局部细节控制的一些方法
- 函数式编程思想的基础应用
函数式编程是一种编程范式,我们常见的编程范式有命令式编程(Imperative programming) ,函数式编程,逻辑式编程,常见的面向对象编程是也是一种命令式编程。
命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指令),表达式(内存引用和算术运算)和控制语句(跳转指令),一句话,命令式程序就是一个冯诺依曼机的指令序列。
而函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式。
函数式编程的本质
函数式编程中的函数这个术语不是指计算机中的函数(实际上是Subroutine),而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。
比如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都是不变的。相同的输入总是得到相同的输出,复用性好。
1、表达式化
函数式编程的思维就是首先转变我们传统的编程观念。去可变量,去循环,把命令式改成声明式
命令式:
声明式
2、高阶逻辑
用了函数式,思维就要从循环、赋值这些低阶逻辑转移成高阶的思考问题。函数式又叫声明式,也就是需要做什么操作,只需要说一下就行,而非写个遍历,做个状态判断.
用函数式就不需要考虑这样,你不知道函数式的列表是怎么遍历的,中间向两边? 从后往前?这也是为何函数式适合并发的原因之一,你想知道列表中大于3的数有多少,只要,list.count(_ > 3) 而不是写循环,你可以直接写你的业务,不要拘泥于细节,有点像sql, 你需要什么告诉电脑就行,你或许会问,count foreach filter 这些函数怎么来的? 因为有了他们你才不需要写循环,他们把你留在高阶逻辑中。
3、组合子逻辑 或又叫 自底向上的设计
面向对象是自顶向下的设计,函数式是自底向上的设计,也就是先定义最基本的操作,然后不断组合,不断堆积以满足你的所有需要,如sql定义了select, from, where…这几个组合子,来满足你的查询需求,同理函数式语言会提供foreach, map等组合子(操作)来满足你的需求,所以你必须自下而上的设计你的代码结构,并且满足你的需求,当你只用组合子写代码时,你会发现你写的全是高阶逻辑
总结:
函数式思维,其实就是组合子逻辑,用简单的几个函数组合来构建复杂逻辑,始终以高阶的角度去表达问题,而非依赖副作用。