在工作中,我们可能碰到这样的问题
(1)用户在搜索的时候,在不停敲字,如果每敲一个字我们就要调一次接口,接口调用太频繁,给卡住了。
(2) 用户在阅读文章的时候,我们需要监听用户滚动到了哪个标题,但是每滚动一下就监听,那样会太过频繁从而占内存,如果再加上其他的业务代码,就卡住了。
解决的办法
所以,这时候,我们就要用到 防抖与节流 了。
那么,讲到(1) 防抖与2()节流,我们可以顺带探秘下 重绘与回流。
说起 重绘与回流,我们就顺带把 浏览器输入 URL 后发生的事情 也关注一下,从而引出 DNS、TCP 等知识点,最终串起来构成本文的轮廓。
1.防抖
##1:任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行。
:通俗的讲 结合上面的代码,我们可以了解到,在触发点击事件后,如果用户再次点击了,我们会清空之前的定时器,
重新生成一个定时器。意思就是:这件事儿需要等待,如果你反复催促,我就重新计时!多用于1.表单的提交 2.模糊搜索 。。。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>防抖</title>
</head>
<body>
<button id="debounce">点我防抖!</button>
<script>
window.onload = function() {
// 1、获取这个按钮,并绑定事件
var myDebounce = document.getElementById("debounce");
myDebounce.addEventListener("click", debounce(sayDebounce));
}
// 2、防抖功能函数,接受传参
function debounce(fn) {
// 4、创建一个标记用来存放定时器的返回值
let timeout = null;
return function() {
// 5、每次当用户点击/输入的时候,把前一个定时器清除
clearTimeout(timeout);
// 6、然后创建一个新的 setTimeout,
// 这样就能保证点击按钮后的 interval 间隔内
// 如果用户还点击了的话,就不会执行 fn 函数
timeout = setTimeout(() => {
fn.call(this, arguments);
}, 1000);
};
}
// 3、需要进行防抖的事件处理
function sayDebounce() {
// ... 有些需要防抖的工作,在这里执行
console.log("防抖成功!");
}
</script>
</body>
</html>
2.节流
##1节流:指定时间间隔内只会执行一次任务。
:通俗的讲 结合下面的代码 我们可以了解到,在触发事件之后,如果用户再次点击了事件,我们会先判断前面事件是否已经完成,
如果没有完成 就会让其等待 直到在设置的指定时间之内 再次触发排队的任务 直到完全执行完任务中的事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>节流</title>
</head>
<body>
<button id="throttle">点我节流!</button>
<script>
window.onload = function() {
// 1、获取按钮,绑定点击事件
var myThrottle = document.getElementById("throttle");
myThrottle.addEventListener("click", throttle(sayThrottle));
}
// 2、节流函数体
function throttle(fn) {
// 4、通过闭包保存一个标记
let canRun = true;
return function() {
// 5、在函数开头判断标志是否为 true,不为 true 则中断函数
if(!canRun) {
return;
}
// 6、将 canRun 设置为 false,防止执行之前再被执行
canRun = false;
// 7、定时器
setTimeout( () => {
fn.call(this, arguments);
// 8、执行完事件(比如调用完接口)之后,重新将这个标志设置为 true
canRun = true;
}, 1000);
};
}
// 3、需要节流的事件
function sayThrottle() {
console.log("节流成功!");
}
</script>
</body>
</html>
防抖,节流最终引起的深层次考虑就是
1.这个操作什么益处那 ???
1.减少耗能 降低cpu
2.我们能这样做成功的原因是什么???我们需要了解哪些需要用到的理论???
1.从输入url到页面展现的整个过程浏览器做了哪些事情???
2.其中js css html 进行了哪些贡献以及在页面展现的整个过程中的其运行的原理是什么???
2.异步与同步
3.重绘与回流
在说浏览器渲染页面之前,我们需要先了解两个点,一个叫 浏览器解析 URL,另一个就是本章节将涉及的 重绘与回流:
(1)重绘(repaint):当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要 UI 层面的重新像素绘制,因此损耗较少。
》常见的重绘操作有:
1.改变元素颜色
2.改变元素背景色
3.总结:当页面中元素样式的改变并不影响它在文档流中的位置时触发的就是重绘的操作
(2)回流(reflow):又叫重排(layout)。当元素的尺寸、结构或者触发某些属性时,浏览器会重新渲染页面,称为回流。此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作。
》常见的重绘操作有:
1.页面首次渲染
2.浏览器窗口大小发生改变
3.元素尺寸或位置发生改变
4.元素内容变化(文字数量或图片大小等等)
5.元素字体大小变化
6.添加或者删除可见的DOM元素
7.激活CSS伪类(例如::hover)
8.查询某些属性或调用某些方法
9.总结:当页面中的一些元素的尺寸 布局 以及影响到位置发生变化 触发的就是回流操作
##1
**1通过重绘以及回流的引起原因我们可以了解到 重绘对耗能方面还不算太严重,而回流则是整个布局的变动类似于二次渲染
耗能可想而知 当然 避免我们没有办法避免 但是减少还是可以的
CSS>>>>>
1.避免使用table布局。
2.尽可能在DOM树的最末端改变class。
3.避免设置多层内联样式。
4.将动画效果应用到position属性为absolute或fixed的元素上。
5.避免使用CSS表达式(例如:calc())。
Javascript>>>>>
1.避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
2.避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
3.也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
4.避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
5.对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
4.浏览器解析 URL
上面提到URL解析的问题 下面将会把URL的解析过程完整的展现在大家的面前
1.从浏览器输入 URL 到渲染成功的过程中,究竟发生了什么?
2.浏览器渲染过程中,发生了什么,是不是也有重绘与回流?
接下来我们会进行讲解URL输入到页面展示浏览器做了哪些???
1.用户输入URL地址
2.浏览器首先进行DNS域名解析
3.通过HTTP协议进行三次握手操作
4.浏览器向服务器发起报文请求
5.服务器对浏览器发起的请求进行报文响应
6.浏览器拿到服务器返回的文档 进行页面的渲染以及展示
7.完整的页面展现在我们面前
URL完整流程图
5.DNS 域名解析
1.什么是 DNS 解析,它是怎么个流程?
DNS(Domain Name System)是 域名系统 的英文缩写,提供的服务是用于将主机名和域名转换为 IP 地址的工作:
域名:http://www.baidu.com <---> DNS <---> IPV4:192.168.16.11
所以,当用户在浏览器输入 http://www.baidu.com 时,DNS 经历了以下步骤:
1.浏览器根据地址,在自身缓存中查找 DNS(域名服务器) 中的解析记录。如果存在,则直接返回 IP 地址;如果不存在,则查找操作系统中的 hosts 文件是否有该域名的 DNS 解析记录,如果有就返回。
2.在条件 1 中的浏览器缓存或者操作系统的 hosts 文件中都没有这个域名的 DNS 解析记录,或者已经过期,则向域名服务器发起请求解析这个域名。
2.先向本地域名服务器中请求,让它解析这个域名,如果解析不了,则向根域名服务器请求解析。
3.根服务器给本地域名服务器返回一个主域名服务器。
4.本地域名服务器向主域名服务器发起解析请求。
5.主域名服务器接收到解析请求后,查找并返回域名对应的域名服务器的地址。
6.域名服务器会查询存储的域名和 IP 的映射关系表,返回目标 IP 记录以及一个 TTL(Time To Live)值。
7.本地域名服务器接收到 IP 和 TTL 值,进行缓存,缓存的时间由 TTL 值控制。
8.将解析的结果返回给用户,用户根据 TTL 值缓存在本地系统缓存中,域名解析过程结束。
6.浏览器渲染页面
浏览器解析文档资源并渲染页面是个怎样的流程?
(1)浏览器通过 HTMLParser 根据深度遍历的原则把 HTML 解析成 DOM Tree。
(2)浏览器通过 CSSParser 将 CSS 解析成 CSS Rule Tree(CSSOM Tree)。
(3)浏览器将 JavaScript 通过 DOM API 或者 CSSOM API 将 JS 代码解析并应用到布局中,按要求呈现响应的结果。
(4)根据 DOM 树和 CSSOM 树来构造 render Tree。
(5)layout:重排(也可以叫回流),当 render tree 中任一节点的几何尺寸发生改变,render tree 就会重新布局,重新来计算所有节点在屏幕的位置。
(6)repaint:重绘,当 render tree 中任一元素样式属性(几何尺寸没改变)发生改变时,render tree 都会重新画,比如字体颜色,背景等变化。
(7)paint:遍历 render tree,并调动硬件图形 API 来绘制每个节点。
RenderTree
网友评论