如何写好JS:举例白天黑夜模式
版本1:通过js直接操作css属性。
缺点:违反各司其职;没有足够的信息(为什么改变)
版本2: js修改className,具体样式交给css
优点:js代码简洁;className可以体现功能
版本3:纯css实现(因为是纯展示页面)
实现:
<input id="light" type="checkbox"></input> //样式隐藏#light { display: none;}
<label for="light"> //绑定,使得点击label时相当于点击到了input
#light:checked + #main {//修改样式
background-color: #000;
color: #fff;
}
优点:可维护性高
缺点:兄弟选择器有的浏览器不支持
复杂UI组件设计:轮播图
步骤1:结构设计
列表结构使用ul\li、
使用 css 绝对定位将图片重叠在同一个位置、
切换采用transition
步骤2:API设计
getSelectedItem():获得选中的元素
getSelectedItemIndex():获得选中的元素是列表中第几个元素(小圆点)
slide To() :解决鼠标移动到小圆点时跳转到哪一页
slide Next():鼠标点击跳转到上一页
slide Previous():鼠标点击跳转到下一页
实现:(不含交互结构:小圆点,左右切换)
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');
setInterval(() => { /*定时轮播*/
slider.slideNext()
}, 3000)
步骤3:控制流结构
控制结构:小圆点与左右切换按钮
圆点控制:圆点mouseover时:确定顺序跳转,并且停止定时器;mouseout时恢复定时器
左右切换控制: 停止计时、slide Next()\ slide Previous()、开始计时
自定义事件:(slideTo中定义):实现圆点移动显示,与图片对应
const detail = {index: idx}
const event = new CustomEvent('slide', {bubbles:true, detail}
this.container.dispatchEvent(event)
圆点绑定事件:
this.container.addEventListener('slide', evt => {
const idx = evt.detail.index
const selected = controller.querySelector('.slide-list__control-buttons--selected');
if(selected) selected.className = 'slide-list__control-buttons';
buttons[idx].className = 'slide-list__control-buttons--selected';
})
优化1:插件/依赖注入:
避免插件和组件的强耦合,降低耦合度。比如直接移除某一块(此时无法去除html)
优化2:改进插件/模版化(只需要维护一处地方即可)
插件由functino变为object(包含两个方法render与action)、封装了HTML和JS,提高可维护性
优化3:组件模型抽象
局部细节控制
例1:逐渐消失的方块
block.onclick = function(evt){
console.log('hide');
evt.target.className = 'hide';
setTimeout(function(){
document.body.removeChild(block);
}, 2000);
};
分析:click可以点击多次,remove后会没有节点。则会报错
解决1:限制只能访问一次
block.onclick = function(evt){
block.onclick = null;
console.log('hide');
evt.target.className = 'hide';
setTimeout(function(){
document.body.removeChild(block);
}, 2000);
};
新浏览器方法:block.addEventListener('click',()=>{},{once:true})
只执行一次的方法
抽象一个once函数:
function once(fn){
return function(...args){
if(fn){
let ret = fn.apply(this, args);
fn = null;
return ret;
}
}
}
节流、防抖函数
消费者:用于连击
reduce方法
toggle
使用生成器
总结
如何写好 JavaScript?
各司其职:JavaScript 尽量只做状态管理
结构、API、控制流分离设计 UI 组件
插件和模板化,并抽象出组件模型
运用过程抽象的技巧来抽象并优化局部 API
常用的HTTP知识
请求、响应报文结构
请求类型
GET获取一个资源内容
POST新增一个资源
PUT更新资源内容
DELETE删除资源
OPTIONS根据返回判断是否有对其请求的权限
HEAD只返回 head,不返回实体内容
PATCH更新部分内容
状态码:
1xx请求已接受,需要继续处理
2xx请求已经正确处理
3xx重定向
4xx客户端错误
5xx服务端错误
URL
受限字符:%/.#?;:$+@&= 以及非US-ASCⅡ字符集字符
cookie
path
domain (hostonly*)
expires (max-age)
secure
httponly:防止XSS
SameSite:防止CSRF
Content-Type:
(请求头)标识提交数据的类型;(响应头)标识返回内容的类型
application/x-www-form-urlencoded
multipart/form-data
application/json
text/xml
性能优化:
keep-alive
HTTP 1.0 原本不支持 Keep-Alive,后来扩充了 Connection: Keep-Alive
HTTP 1.1 默认支持 Keep-Alive,除非显式指明 Connection: close
减少网络传输大小:压缩
content-encoding:gzip
缓存
http2/http3
http2:
二进制传输
多路复用
头部压缩
server push
HTTP3
基于 QUIC 协议(UDP)
HTTP 抓包工具
Wireshark
Fiddler
Firebug for Firefox
Chrome 开发者工具
IE8+ 自带的开发者工具
HTTP 发包工具
telnet / curl
Fiddler *
Tamper for Firefox
Postman for Chrome *
Paw for OSX