- 减少http请求
避免小资源多次请求,可以将多个资源合并为一个资源,例如js, 因为发送网络请求很耗费时间 - css放在头部,js放在body底部
有利于更快的渲染出页面,css会阻塞js的执行,因为只有cssom创建后才会执行js,而js会阻塞整个渲染进程
不过js放在头部也可以,但是要想不阻塞渲染的话可以加上defer或者async属性 - 使用字体图标代替图片图标
字体图标较图片图标来说会小很多,节省传输开销 - 减少重绘重排
避免使用js频繁操作样式, 可以使用class替换的方式
多次对dom操作可以借助documentFragment或者操作display:none的元素, 操作完成后一并添加到或显示
使用transform和opacity实现动画可减少重绘重排, 二者是有和合成器单独处理的属性。 - 在判断语句情况很多时, 避免使用if-else,可以使用switch-case或策略者模式,易于阅读维护
- 避免使用定时器设置动画
定时器设置动画由于需要指定时间,并且对于时间的把控不当会造成页面动画卡顿。
可以使用requestAnimationFrame来设置动画,前面也已经介绍过这个针对于帧来执行逻辑的api - 对于静态资源,可以放在cdn上
cdn是一组分布在各地区的服务器,因此可以根据哪个服务器与用户最近使用哪个服务器为用户服务 - 可以使用web workers
对于耗时很长的逻辑,可以使用web workers开启额外的线程去执行,其不会阻塞主线程js代码的执行。
注意:- 有同源限制
- 无window对象,其全局对象或者this就是它自己
- 无法访问dom节点
- 与主线程之间数据的传递是通过拷贝传递的
简单使用
// 主线程
var worker = new Worker('worker.js') // work线程的URI
worker.postMessage([10, 24])
worker.onmessage = function(e) {
console.log(e.data)
}
// Worker 线程, worker.js
onmessage = function (e) {
if (e.data.length > 1) {
postMessage(e.data[1] - e.data[0])
}
}
// 停止worker线程可以在主线程调用实例的.close方法
worker.terminate()
// 或者worker调用自身的close()
this.close()
-
事件相关
-
事件委托
给多个子元素绑定同一个事件不如直接给父元素绑定事件, 借助事件委托的方式来节省绑定事件的内存开销 -
防抖
频繁触发事件时,例如表单输入检查,只有当停止或暂停输入后一小段时间后才会执行检查,而不去一直输入一直检查
注意需要绑定一下thisfunction debounce(callback, delay) { let timer = null return function(){ // 这里需要清除上一次的定时器,一直触发一直清除, 知道停止触发时才绑定定时器执行 clearTimeout(timer) let context = this timer = setTimeOut(()=> { callback.call(context) }) } }
-
节流
防抖只针对频繁触发且会暂停会停止的操作, 如果对于一些一直触发并且在不停止时就需要回应的情况下, 比如判断鼠标滚动距离来实现懒加载时, 对鼠标滚动可以使用节流
实现方式
定时器
对定时器定时, 触发时没到时间即定时器还存在就结束, 否则就执行逻辑并且清除定时器供下次重新计时。/* 如果定时器还存在就退出不执行, 不存在的话就绑定一个定时器 绑定的函数会在延迟时间到达后执行, 执行后需要将timer = null 注意不是清除定时器, 而且设置为null, 因为timer保存的定时器的id值 清除了定时器但是id值还是在的 */ function throttle(callback){ let timer = null return function(){ if(timer) return let context = this timer = setTimeout(() => { callback.call(context) timer = null }) } } ```
时间戳
时间戳的方式不需要使用定时器, 判断当前时间与上一次执行的时间的差值是否 > 基准值, 若大于则执行并设置基准值为当前时间, 否则结束。function throttle(callback, delay){ let pre = 0 return function(){ let now = Date.now() if(now - pre > delay) { callback.call(this) pre = now } } }
-
-
图片懒加载
首先给图片一个资源占位属性,等到图片出现在视口上才开始加载,否则所有图片同时加载会消耗大量网络资源。
实现懒加载主要两种方式,一个是借助元素的样式属性判断是否到达视口,另一种是使用js封装的api。
clientHeight,offsetTop,scrollTop实现
原理:
clientHeight是容器视口的高度,scrollTop是滚动条高度,两者的和是元素若没有在滚动条里实际到容器视口顶部的距离,而offectTop就是元素到父节点的偏移距离,如果clientHeight + scrollTop ≤ offectTop则说明元素出现在了视口,即可以显示了。
注意: 借助一个n来保存当前加载的图片的位置, 这样就不用每次都从头循环遍历了<style> #div{ border: 2px solid black; height: 500px; overflow: auto; } .div { margin-bottom: 5px; background-color: aquamarine; width: 200px; height: 300px; } </style> <body> <ul id="list"> <li><img data-src="./images/iu.jpg" src='./images/lazy.png' alt=""></li> <li><img data-src="./images/iu1.jpg" src='./images/lazy.png' alt=""></li> <li><img data-src="./images/iu2.png" src='./images/lazy.png' alt=""></li> <li><img data-src="./images/iu3.jpg" src='./images/lazy.png' alt=""></li> <li><img data-src="./images/iu4.jpg" src='./images/lazy.png' alt=""></li> <li><img data-src="./images/iu5.jpg" src='./images/lazy.png' alt=""></li> <li><img data-src="./images/iu6.jpg" src='./images/lazy.png' alt=""></li> <li><img data-src="./images/iu7.jpg" src='./images/lazy.png' alt=""></li> <li><img data-src="./images/iu8.jpg" src='./images/lazy.png' alt=""></li> <li><img data-src="./images/iu9.jpg" src='./images/lazy.png' alt=""></li> <li><img data-src="./images/iu10.jpg" src='./images/lazy.png' alt=""></li> <li><img data-src="./images/zzf_01.jpg" src='./images/lazy.png' alt=""></li> </ul> </body> let list = document.getElementById('list') let imgs = document.getElementsByTagName('img') let len = imgs.length let n = 0 function lazyLoad(){ let clientHeight = list.clientHeight let scrollTop = list.scrollTop for(let i = n; i < len; i++) { let tempSrc = imgs[i].getAttrbute('data-src') if(imgs[i].offset <= clientHeight + scrollTop ) { imgs[i].src = tempSrc n = i // 缓存当前加载到哪里了 } } } // 使用防抖优化 list.addEventListenr('scroll', throttle(lazyLoad, 500))
上述可以正常实现, 经过n = i优化后也不至于执行重复之前已经加载的了, 但是对于往返滚动, 还是会执行for循环进行判断
可以使用js的api的IntersectionObserver, 传入元素的话, 其可以观察是否在视口中显示或者移出时会触发相应的事件
/*
首先创建一个观察对象, 其回调中接收的参数就是被观察的对象,可以是多个,给每个绑定事件
被观察对象的intersectionRation是一个值, 当出现在视口时, 其>0, 反之
可以设置当已经触发加载后, 就移除被观察对象里
*/
let obser = new IntersectionObserver((imgs) => {
for(let i = 0; i < imgs.length; i++) {
let img = imgs[i]
if(img.inersectionRatio > 0) {
img.src = img.getAttrbute('data-src')
obser.unobserve(img)
}
}
})
obser.observe(Array.from(imgs))
- webpack相关
使用缓存
-
在资源未发生改变时, 直接走缓存, 并用contentHash值拼接文件名, 防止内容改变了也走缓存。
-
babel处理时, 很多兼容性处理的代码并不需要重新构建, 可以开启babel的缓存cacheDirectory: true
代码压缩css压缩使用OptimizeCssAssetsWebpackPlugin 插件
html压缩使用html-webpack-plugin
js在生产环境下自动压缩