Skip to main content

写好 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 行,而且这个代码也不是很复杂,但是却很多人使用,完全可以自己写一个。

这给我们的启示是:

  • 不要过度依赖第三方库
  • 对自己的代码负责