细说节流(Throttle)和防抖(Debounce)

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"节流(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Throttle","attrs":{}}],"attrs":{}},{"type":"text","text":")和防抖(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Debounce","attrs":{}}],"attrs":{}},{"type":"text","text":")对于前端开发人员来说应该是十分熟悉的,节流(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Throttle","attrs":{}}],"attrs":{}},{"type":"text","text":")和防抖(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Debounce","attrs":{}}],"attrs":{}},{"type":"text","text":")是两种可以节省性能的编程技术,两者的目的都是为了优化性能,提高用户体验,都是基于 DOM 事件限制正在执行的 JavaScript 数量的方法。但两者的有什么不一样呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"节流会强制执行一个函数在一段时间内可以被调用的最大次数。如“","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"最多每 100 毫秒执行一次此函数","attrs":{}},{"type":"text","text":"”。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假设在正常情况下,会在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"10","attrs":{}}],"attrs":{}},{"type":"text","text":" 秒内调用此函数 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"1,000","attrs":{}}],"attrs":{}},{"type":"text","text":" 次。如果将其限制为每 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"100","attrs":{}}],"attrs":{}},{"type":"text","text":" 毫秒仅一次,则该函数最多只会执行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"100","attrs":{}}],"attrs":{}},{"type":"text","text":" 次。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"(10s * 1,000)","attrs":{}}],"attrs":{}},{"type":"text","text":" = ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"10,000 ms","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"10,000 ms / 100 ms","attrs":{}}],"attrs":{}},{"type":"text","text":" 限制 = ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"100","attrs":{}}],"attrs":{}},{"type":"text","text":" 个最大调用","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"防抖强制一个函数在一段时间内没有被调用之前不会被再次调用。如“","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"仅当 100 毫秒过去了而没有被调用时才执行此函数","attrs":{}},{"type":"text","text":"”。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也许一个函数在短时间内被调用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"1000","attrs":{}}],"attrs":{}},{"type":"text","text":" 次,分散在3秒内,然后停止调用。如果在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"100","attrs":{}}],"attrs":{}},{"type":"text","text":" 毫秒的时间内启动,这个功能只会在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"3.1","attrs":{}}],"attrs":{}},{"type":"text","text":" 秒的时间内启动一次。每次在突发事件期间调用该函数时,它都会重置恢复计时器。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"区别是什么?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这些概念的一个主要用例是某些 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"DOM","attrs":{}}],"attrs":{}},{"type":"text","text":" 事件,例如滚动和调整大小。例如,如果将滚动处理程序附加到一个元素,并将该元素向下滚动 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"5000","attrs":{}}],"attrs":{}},{"type":"text","text":" 像素,可能会看到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"100","attrs":{}}],"attrs":{}},{"type":"text","text":" 多个事件被触发。如果事件处理程序做了大量工作(例如繁重的计算和其他 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"DOM","attrs":{}}],"attrs":{}},{"type":"text","text":" 操作),可能会看到性能问题(卡顿)。如果可以减少执行该处理程序的次数,而不会对经验造成太大影响,那么这可能是值得的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"常用的场景如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"等到用户停止调整窗口大小","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在用户停止输入之前不要触发 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AJAX","attrs":{}}],"attrs":{}},{"type":"text","text":" 事件","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"监测或者获取页面的滚动位置,最多每 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"100ms","attrs":{}}],"attrs":{}},{"type":"text","text":" 响应一次","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在应用中拖动元素时确保良好的性能","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"怎么做?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"节流(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Throttle","attrs":{}}],"attrs":{}},{"type":"text","text":")和防抖(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Debounce","attrs":{}}],"attrs":{}},{"type":"text","text":")两个函数都内置到 ","attrs":{}},{"type":"link","attrs":{"href":"https://lodash.com/","title":"","type":null},"content":[{"type":"text","text":"Lodash","attrs":{}}]},{"type":"text","text":" 脚本库中,无需自己实现。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"节流(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Throttle","attrs":{}}],"attrs":{}},{"type":"text","text":")场景","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"$(\"body\").on(\"scroll\", _.throttle(function() {\n // 处理逻辑\n}, 100));\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"防抖(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Debounce","attrs":{}}],"attrs":{}},{"type":"text","text":")场景","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"实际生活中,如百度搜索,输入文本后会出现下拉选择,这个过程一般绑定文本事件 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"keypress","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下图描述了使用防抖(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Debounce","attrs":{}}],"attrs":{}},{"type":"text","text":")前状态的性能监控捕获,每次 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"keypress","attrs":{}}],"attrs":{}},{"type":"text","text":" 引发事件时,它都会触发搜索引擎请求数据并将结果呈现在屏幕上。事实上,这些结果并没有被用户看到,因为它们已经被最新 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"keypress","attrs":{}}],"attrs":{}},{"type":"text","text":" 事件的后续结果覆盖了,屏幕上仅呈现最新结果。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b7/b7335e62622e15ebc5ee1b89cc634bda.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面是使用防抖(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Debounce","attrs":{}}],"attrs":{}},{"type":"text","text":")进行优化后的结果,如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d1/d19efdce6c365c3efb579fb2590dc300.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如所看到的,搜索过程和结果呈现只调用一次(当用户完成输入时),没有重复调用不重要的功能,如布局渲染、内存处理、DOM元素管理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面是以搜索为场景,展示 AJAX 的请求情况:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b7/b7ef3e4850fd5f01cf3952d7af2ebf9f.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"还有下面的窗口大小调整场景,如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"$(window).on(\"resize\", _.debounce(function() {\n // 处理逻辑\n}, 100));\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在现代浏览器中,可以使用 ","attrs":{}},{"type":"link","attrs":{"href":"https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame","title":"","type":null},"content":[{"type":"text","text":"requestAnimationFrame","attrs":{}}]},{"type":"text","text":",告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"总结","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"节流(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Throttle","attrs":{}}],"attrs":{}},{"type":"text","text":")和防抖(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Debounce","attrs":{}}],"attrs":{}},{"type":"text","text":")这两个来自于需要延迟功能的执行,因为用户不想进行过多的HTTP请求。如今,这些是提高 Web 性能的重要方法。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章