性能优化原则
- 多使用内存、缓存或其他方法
- 减少 CPU 计算量,减少网络加载耗时
- (适用于所有编程的性能优化—— 空间换时间)
优化方式
-
让加载更快
- 减少资源体积:压缩代码
- 减少访问次数:合并代码,SSR服务器端渲染,缓存
- 使用更快的网络:CDN(根据区域匹配服务器,更快链接网络)
-
让渲染更快
- CSS 放在 head,JS 放在body 末尾
- 尽早开始执行JS,用 DOMContentLoaded 触发
- 懒加载(图片懒加载,上划加载更多)
- 对 DOM 查询进行缓存
- 频繁 DOM 操作,合并到一起插入 DOM结构
- 节流 throttle 和 防抖 debounce
示例
资源合并
缓存
- 静态资源加 hash 后缀,根据文件内容计算 hash
- 文件内容不变,则 hash 不变,则 url不变
- url 和文件不变,则会自动触发 http 缓存机制,返回 304
CDN
- CDN是根据地域进行网络请求的
- 静态服务一般都是用 CDN 来做的
- CDN 也完全符合304缓存机制
SSR
- 服务器端渲染:将网页和数据一起加载,一起渲染
- 非SSR(前后端分离):先加载网页,在加载数据,在渲染数据
- 早先的JSP ASP PHP,现在的(借助npm)vue React SSR
节流 throttle
场景示例
- 拖拽一个元素时,要随时拿到该元素被拖拽的位置
- 直接用 drag 事件,则会频繁触发,很容易导致卡顿
- 节流:无论拖拽速度多快,都会每隔100ms 触发一次
<body>
<button id="btn1"></button>
<div id="div1" draggable="true">可拖拽</div>
<script src="./js/throttle.js"></script>
</body>
const div1 = document.getElementById('div1')
function throttle(fn, delay = 1000) {
let timer = null
return function(){
if(timer) {
return
}
timer = setTimeout(()=> {
// 这里用apply()方法,是因为fn()会传入参数,若不改变this指向,参数e就只会被传到function()里,而不是fn()
// 例如
// div1.addEventListener('drag',function(event) {
// //event是被传送到return的functio()
// })
fn.apply(this, arguments)
// 在这里置空而不是外面,因为drag事件在不停触发时,要保证每隔一个delay才返回结果,所以在这里执行完fn()之后,timer=null,就不会一直触发if(timer)
timer = null;
},delay)
}
}
div1.addEventListener('drag',throttle(function(e) {
console.log(e.offsetX,e.offsetY);
}))
防抖 debounce
- 监听一个输入框,文字变化后就会触发 change 事件
- 直接用 keyup 事件,则会频繁触发 change 事件
- 防抖:用户输入结束或暂停时,才会触发 change 事件
<body>
<input type="text" id="input1" >
<script src="./js/debounce.js"></script>
</body>
function debounce(fn,delay = 500) {
// timer 是闭包中的
let timer = null
return function() {
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(()=> {
fn(this, arguments)
timer = null
},delay)
}
}
const input1 = document.getElementById('input1')
input1.addEventListener('keyup',debounce(function() {
console.log(input1.value);
}))
/**
* 监听的是keyup事件,即按键松开后触发
* 当上次 keyup 事件触发后都是时间小于delay,按键重新输入,此时timer就应该被清空,重新触发setTimeout(), setTimeout()执行完成,timer置空
*/