前言
在前端开发领域,性能优化始终是提升用户体验的关键环节。其中,节流(throttling)作为一种常见的优化手段,能够有效地控制函数执行的频率,避免在短时间内高频触发事件导致的资源浪费和性能下降。同时,深入了解浏览器底层原理,尤其是内核的运作机制,对于开发者来说至关重要,它不仅关乎代码的兼容性和性能,还能帮助我们更好地理解前端技术的底层实现。
正文
节流与防抖不同
防抖适用于那些需要在用户操作完全停止后才进行处理的情况
而节流的目的是限制函数的执行频率,确保在一定时间内函数最多只能执行一次。无论事件触发多少次,只要在设定的时间间隔内,函数都不会再次执行。一旦过了这个时间间隔,函数就会执行,并重新计时。
-
节流适用于那些即使在用户持续操作中也需要定期执行的任务,例如:
- 滚动事件:在用户快速滚动时,每隔一定时间才执行一次相关逻辑,避免过度消耗资源。
- 鼠标移动:在用户鼠标移动时,每隔一定时间才更新位置信息,避免过多的DOM操作。
- 触摸屏滑动:在用户滑动屏幕时,控制更新频率,确保响应速度的同时避免资源浪费。
-
防抖关注的是“静止”,即在事件完全停止触发一段时间后才执行一次函数。
-
节流关注的是“频率”,即在规定时间内,无论事件触发多少次,函数都只能执行一次。
开始实现
而在处理这类如滚动、调整窗口大小、键盘按键等高频事件时,我们前面提到的闭包和定时器就显得尤为重要了起来
节流(throttle)
我们先写出一个HTML页面让其显示一个不带节流的和一个带节流的输入框
<body>
<div class="row">
<div>
没有节流的input <input type="text" id = "inputa"/>
</div>
<div>
节流后的input <input type="text" id = "inputc"/>
</div>
</div>
</body>
我们先将不带节流的输入框的功能写出js内容
<script>
const inputa = document.getElementById('inputa')
const ajax = (content) => {
console.log(`ajax request ${content}`);
}
inputa.addEventListener('keyup', (e)=>{
ajax(e.target.value)
})
</script>
而这样频繁的调用函数,就会导致以下几点:
- 性能瓶颈:在用户快速或连续触发某些事件(如滚动、鼠标移动、键盘输入等)时,如果不加以控制,可能会导致同一函数被高频调用,这将极大地消耗CPU资源,降低应用的响应速度,甚至可能导致浏览器卡顿或崩溃。
- 资源浪费:对于涉及网络请求的操作,如实时搜索建议、上传数据等,如果不节流,每一次用户微小的操作都可能触发一次网络请求,这不仅增加了服务器负担,也浪费了带宽资源。
- 用户体验下降:在一些敏感的用户交互场景中,如调整窗口大小、拖拽元素等,如果没有适当的节流,应用的响应可能会变得迟钝,影响用户的操作流畅度,降低整体的用户体验。
所以我们需要节流,这也是面试中常考的知识点
const inputc = document.getElementById('inputc')
const throttle = (func,delay)=> {
let last, deferTimer
return (args) => {
let now = +new Date();
if(last && now < last + delay){
clearTimeout(deferTimer)
deferTimer = setTimeout(function() {
last = now
func(args)
},delay)
}else {
last = now // 第一次时间
func(args) // 先执行一次
}
}
}
let throttledFunc = throttle(ajax, 1000)
inputc.addEventListener('keyup', (e) => {
let value = e.target.value
throttledFunc(value)
})
重点来了!
我们先拿到输入框的值
接着定义一个throttle函数,传入两个参数,一个是func
需要执行的函数,另一个是delay
需要空余的时间
我们在函数体内返回一个函数,形成闭包
函数体内我们先放着,先写调用
定义一个变量,传入throttle
函数的返回值,也是一个函数
然后,对inputc
写一个事件监听,addEventListener
,对keyup
做触发,获取到输入框的输入值,并传给throttle
的返回值
最后就是特别巧妙的地方了——各函数的链式调用与throttle
函数的精妙算法
let throttledFunc = throttle(ajax, 1000)
意味着,将ajax函数和1000ms这个值传给了throttle
函数,分别作为了func
和delay
,
而在throttle
函数内,声明了两个变量:
last
用于存储上一次实际执行func
函数的时间戳。deferTimer
是一个setTimeout
的引用,用于管理延时任务。a
返回了一个新的函数,形成了闭包,获取到传入的值args
紧接着获取当前时间的时间戳new Date()
真正的精妙之处便在这个if
上
这个条件检查既考虑了 last
是否已经被初始化(即函数是否至少执行过一次),也检查了自上次执行以来是否已经过去了 delay
指定的时间。这种双重检查确保了函数在首次调用时会立即执行,并在后续调用中根据时间间隔进行限制。
当条件满足时,即在 delay
时间内再次触发函数调用,代码会先清除已存在的延时任务(通过 clearTimeout(deferTimer)
),然后重新设置一个新的延时任务(通过 setTimeout
)。这样做的好处是,即使在 delay
时间内有多个连续的事件触发,函数也只会被安排在最后一个延时任务结束后执行一次,避免了不必要的重复执行。
这种实现方式不仅限定了函数的最小执行间隔,还允许在首次调用时立即执行,提高了用户交互的即时响应性。同时,通过合理使用 setTimeout
和 clearTimeout
,避免了不必要的函数调用,提升了整体性能。
最后,我们将调用这个返回函数写在触发函数 inputc.addEventListener
里也就是说,这个函数只会在我们结束按键时调用一次
确保了 func
不会过于频繁地执行,而是按照 delay
参数指定的时间间隔进行调用。这种节流机制可以有效防止在短时间内重复执行相同的任务,从而节省系统资源,提高应用性能。
怎么样,看着这段代码是不是特别的爽,节流就是如此美妙
小知识点:浏览器底层原理
我们在写前端时,和浏览器一定是紧密相连的,所以我们需要去了解它,了解它干了什么事情
浏览器内核详解
浏览器内核主要由两个核心组件构成:渲染引擎和JavaScript引擎。它们分别负责解析和渲染网页内容,以及执行JavaScript代码。
- 渲染引擎:负责解析HTML、CSS和图像文件等,构建出文档对象模型(DOM)和渲染树,并最终将其渲染到用户的屏幕上。
- JavaScript引擎:专门用于解析和执行JavaScript代码,使网页能够实现动态效果和用户交互。
不同内核特性
- WebKit: 最初由苹果公司为Safari开发,后来Google在Chrome中也采用了WebKit。它具有高性能和跨平台特性,是移动设备上最常用的内核之一。
- Blink: Google基于WebKit开发的独立内核,用于Chrome和其他一些浏览器。Blink通过移除WebKit中的一些冗余模块,显著提升了性能和增强了安全性。
- Trident: 微软Internet Explorer的默认内核,虽然已被EdgeHTML取代,但许多老旧系统和企业环境仍在使用。
- Gecko: Mozilla Firefox的内核,以开源和高度可定制性著称,支持广泛的Web标准。
渲染引擎工作流程
浏览器解析和渲染网页的过程非常复杂,主要包括以下几个阶段:
- HTML解析:浏览器接收到HTML数据后,开始解析HTML文档,构建DOM树。这个过程是增量式的,即浏览器不需要等待整个HTML文档下载完毕就开始解析和渲染。
- CSS解析:同时,CSS解析器读取CSS规则,构建CSSOM(CSS对象模型)。CSS的加载和解析是阻塞的,意味着在CSSOM未构建完成之前,渲染引擎无法继续后续工作。
- 构建渲染树:结合DOM树和CSSOM,浏览器创建一个渲染树,其中包含所有可见元素及其样式信息。注意,这个阶段不会包括那些不可见的元素(如
<script>
、<style>
标签)。 - 布局计算:接下来,浏览器需要确定每个元素在屏幕上的确切位置和尺寸,这一过程称为布局或回流。布局树是基于渲染树构建的,它反映了每个元素的几何信息。
- 绘制:最后,浏览器将布局树的信息转化为实际的像素点,绘制到屏幕上,完成最终的视觉呈现。这个过程称为绘制或重绘。
性能优化与CSS选择器
CSS选择器的性能对渲染速度有着直接的影响。以下是一些关键点:
- 避免使用通用选择器(*) :通用选择器会导致全树遍历,大大增加渲染时间和CPU负载。
- 减少ID选择器的使用:虽然ID选择器速度快,但如果使用不当(如在JavaScript中频繁查询),也会导致性能问题。
- 优先使用类型选择器和类选择器:这些选择器性能较好,因为它们只需要查找DOM树的一级或几级即可找到目标元素。
- 避免复杂的CSS选择器链:长而复杂的CSS选择器链会导致渲染引擎做更多的工作来定位元素,从而影响性能。
总结
节流技术和对浏览器底层原理的深入理解,是提升前端应用性能和用户体验不可或缺的两把钥匙。通过合理运用这些知识,我们可以编写出更加高效、流畅的网页应用。
求点赞评论收藏,有问题随时私信博主!