写好 JavaScript
各司其职
原生 JS 实现一个切换深色模式的功能,我们会怎么做?我们或许使用 JS 来调整 CSS 变量来实现:
const btn = document.getElementById('modeBtn')
btn.addEventListener('click', (e) => {
const body = document.body;
if (e.target.innerText === '深色模式') {
body.style.setProperty('--bg-color', '#000')
body.style.setProperty('--text-color', '#fff')
e.target.innerText = '浅色模式'
} else {
body.style.setProperty('--bg-color', '#fff')
body.style.setProperty('--text-color', '#000')
e.target.innerText = '深色模式'
}
})
但是实际上,这样的代码是不够优雅的,因为我们的 JS 代码和 CSS 代码耦合在了一起,如果我们想要修改 CSS 样式,我们就需要修改 JS 代码,这样的代码不利于维护。我们可以用类名来控制样式,这样的代码就会更加优雅:
const btn = document.getElementById('modeBtn')
btn.addEventListener('click', (e) => {
const body = document.body;
if (e.target.innerText === '深色模式') {
body.classList.add('dark')
e.target.innerText = '浅色模式'
} else {
body.classList.remove('dark')
e.target.innerText = '深色模式'
}
})
这就是各司其职的原则,我们的 JS 代码只负责控制逻辑,而不负责样式,样式的控制交给 CSS 来做,这样的代码更加优雅,也更加容易维护。
具体例子
修改前:
<header>
<button id="modeBtn">🌞</button>
<h1>深浅色模式切换</h1>
</header>
body,
html {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
}
body {
padding: 10px;
box-sizing: border-box;
}
#modeBtn {
font-size: 2rem;
float: right;
border: none;
background: transparent;
}
window.onload=function(){
document.getElementById("file-btn")
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 = '🌞';
}
});
}
修改后:
//html
<header>
<button id="modeBtn"></button>
<h1>深浅色模式切换</h1>
</header>
//css
body,
html {
width: 100%;
height: 100%;
max-width: 600px;
padding: 0;
margin: 0;
overflow: hidden;
}
body {
padding: 10px;
box-sizing: border-box;
transition: all 1s;
}
#modeBtn {
font-size: 2rem;
float: right;
border: none;
outline: none;
cursor: pointer;
background: inherit;
}
body.night {
background-color: black;
color: white;
transition: all 1s;
}
#modeBtn::after {
content: '🌞';
}
body.night #modeBtn::after {
content: '🌜';
}
//js
window.onload=function(){
document.getElementById("file-btn")
const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e) => {
const body = document.body;
if (body.className !== 'night') {
//通过className的'night'来显示深色模式
body.className = 'night';
} else {
body.className = '';
}
});
}
组件封装
组件封装是指将一个功能封装成一个组件,这个组件可以被复用,这样的代码更加优雅,也更加容易维护。常见的组件封装有:
- 轮播图,可以复用在首页、详情页等
- 搜索框,可以复用在首页、详情页等
- 评论组件,可以复用在首页、详情页等
- 登录组件,可以复用在首页、详情页等
- 深色模式切换组件,可以复用在首页、详情页等
组件封装有下面的基本方法:
- 结构设计
- 展示效果
- 行为设计(API、Event)
使组件具备封装性、正确性、扩展性、复用性。
过程抽象
过程抽象是指将用来处理局部细节控制的代码封装成一个函数,以函数式的编程思想来编写代码。
在过程抽象中,我们需要注意的是:
- 函数的功能单一
- 函数的参数尽量少
- 函数的返回值尽量少
扩展:Leftpad 事件
在 2016 年,npm 上有一个名为 left-pad 的包,这个包的作用是在字符串前面补全空格,比如:
leftPad('hello', 10) // ' hello'
这个包的作者在 2016 年 4 月 1 日发布了 1.0.0 版本,但是在 2016 年 4 月 2 日,这个包的作者删除了这个包,这个事件被称为 Leftpad 事件。这个模块下架导致了很多依赖这个模块的项目无法正常运行。实际上,这个代码只有 11 行,而且这个代码也不是很复杂,但是却很多人使用,完全可以自己写一个。
这给我们的启示是:
- 不要过度依赖第三方库
- 对自己的代码负责