Github 代码库
在快节奏的世界中,我们所做的大部分工作都是在网络上完成的,而且速度很快。创建无缝、流畅的用户体验变得更加重要。消费者喜欢运行速度快、没有滞后或延迟的用户界面。实现近乎完美的体验是可能的,尽管很棘手。您听说过事件循环吗?
在 JavaScript 中,事件循环是一个基本概念,它管理代码的执行顺序、收集进程、将指令放入排队的子任务中并高效地运行异步操作。以下是事件循环工作原理的快速分解:
此事件循环不断检查调用堆栈。 JavaScript 代码的执行将继续,直到调用堆栈为空。
事件处理是构建 JavaScript 应用程序的一个非常重要的部分。在这样的应用程序中,我们可能需要将多个事件与 UI 组件相关联。
UI 中有一个按钮,可帮助在表格中填充最新的体育新闻。现在这需要您:
这 3 个进程以同步方式链接在一起。现在,反复按下按钮意味着多次 API 调用 - 导致 UI 被阻塞相当长的时间 - 看似滞后的用户体验。
这是 Debouncing 和 Throttling 等方法的一个很好的用例。对于此类触发一系列复杂事件的事件 - 我们可以使用此类操作来限制调用 API 的次数,或者一般意义上 - 限制我们处理事件的速率。
去抖动:推迟函数的执行,直到自上次事件以来经过指定的冷却时间。
例如:
如果我们对handleOnPressKey()进行反跳2秒,则只有当用户停止按键2秒时才会执行。
代码片段:
let debounceTimer; // Timer reference const handleOnPressKey = () => { console.log("Key pressed and debounce period elapsed!"); }; const debouncedKeyPress = () => { // Clear any existing timer clearTimeout(debounceTimer); // Start a new debounce timer debounceTimer = setTimeout(() => { handleOnPressKey(); // Execute the function after cooldown }, 2000); // Cooldown period of 2000ms }; // Attach debouncedKeyPress to keypress events document.getElementById("input").addEventListener("keypress", debouncedKeyPress);
限制:确保函数在指定时间段内最多调用一次,无论事件发生的频率如何。
例如:
如果我们以2秒间隔限制handleOnScroll(),则该函数最多每2秒执行一次,即使滚动事件在该时间段内触发多次。
代码示例:
let throttleTimer; // Timer reference const handleOnScroll = () => { console.log("Scroll event processed!"); }; const throttledScroll = () => { if (!throttleTimer) { handleOnScroll(); // Execute the function immediately throttleTimer = setTimeout(() => { throttleTimer = null; // Reset timer after cooldown }, 2000); // Cooldown period of 2000ms } }; // Attach throttledScroll to scroll events document.addEventListener("scroll", throttledScroll);
让我们快速看一下 HTML 代码,然后再进入更关键的 script.js
我们使用 TailwindCSS 来快速设计样式。您可以在此处查看他们的文档 Tailwind 文档 - 它对于制作快速原型非常有帮助
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Event Loop Practice</title> <!-- Tailwind CSS CDN --> <script src="https://cdn.tailwindcss.com"></script> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <style> /* Tailwind Extensions (Optional for Customizations) */ body { font-family: 'Inter', sans-serif; } </style> </head> <body> <h3> Why Use Fuse.js? </h3> <p>Fuse.js is a lightweight, customizable library for fuzzy searching. It handles typos and partial matches, offers high performance for large datasets, and has an intuitive API. This will help enhance your search functionality with flexible, user-friendly search experiences. Additionally, this provides you with a CDN link, so it can work right of the bat, no imports or local storage required.</p> <h2>Now let's Code in the Real Deal - The JS</h2> <h4> 1. Task Array and Variables </h4> <pre class="brush:php;toolbar:false">const tasks = new Array ( "Complete Blog on Throttling + Debouncing", "Make a list of 2025 Resolutions", ); let fuse = undefined; let debounceTimer; let throttleTimer;
本节初始化任务数组并声明 Fuse.js、防抖计时器和节流计时器的变量。为了这个项目,我们已经硬编码了一些任务
现在让我们构建 onSubmit 函数。一旦用户单击“提交”箭头,就会触发此功能。它阻止默认表单提交,检索输入值,清除输入字段,将新任务添加到任务数组,并更新任务列表。
let debounceTimer; // Timer reference const handleOnPressKey = () => { console.log("Key pressed and debounce period elapsed!"); }; const debouncedKeyPress = () => { // Clear any existing timer clearTimeout(debounceTimer); // Start a new debounce timer debounceTimer = setTimeout(() => { handleOnPressKey(); // Execute the function after cooldown }, 2000); // Cooldown period of 2000ms }; // Attach debouncedKeyPress to keypress events document.getElementById("input").addEventListener("keypress", debouncedKeyPress);
现在我们需要确保任务提交后,它会在任务列表中更新
let throttleTimer; // Timer reference const handleOnScroll = () => { console.log("Scroll event processed!"); }; const throttledScroll = () => { if (!throttleTimer) { handleOnScroll(); // Execute the function immediately throttleTimer = setTimeout(() => { throttleTimer = null; // Reset timer after cooldown }, 2000); // Cooldown period of 2000ms } }; // Attach throttledScroll to scroll events document.addEventListener("scroll", throttledScroll);
updateList() 函数通过循环遍历任务数组并为每个任务创建列表项来呈现任务列表。每个列表项都包含一个项目符号点和任务文本。
现在我们需要确保列表在第一次加载页面后得到更新。我们还希望在页面加载时初始化 Fuse.js - 并将任务数组与其关联。请记住,我们希望在下拉列表中呈现来自此任务数组的建议。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Event Loop Practice</title> <!-- Tailwind CSS CDN --> <script src="https://cdn.tailwindcss.com"></script> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <style> /* Tailwind Extensions (Optional for Customizations) */ body { font-family: 'Inter', sans-serif; } </style> </head> <body> <h3> Why Use Fuse.js? </h3> <p>Fuse.js is a lightweight, customizable library for fuzzy searching. It handles typos and partial matches, offers high performance for large datasets, and has an intuitive API. This will help enhance your search functionality with flexible, user-friendly search experiences. Additionally, this provides you with a CDN link, so it can work right of the bat, no imports or local storage required.</p> <h2>Now let's Code in the Real Deal - The JS</h2> <h4> 1. Task Array and Variables </h4> <pre class="brush:php;toolbar:false">const tasks = new Array ( "Complete Blog on Throttling + Debouncing", "Make a list of 2025 Resolutions", ); let fuse = undefined; let debounceTimer; let throttleTimer;
const onSubmit = (event) => { //Prevent default event.preventDefault(); const text = document.getElementById("input").value.trim(); document.getElementById("input").value = ""; tasks.push(text); updateList(); }
现在我们需要确保在每个“输入”中我们都会搜索列表以在下拉列表中显示建议。这有 3 个部分:
const updateList = () => { const lists = document.getElementById("taskList"); lists.innerHTML = ""; //Loop through all elements in tasks tasks.forEach(task => { const taskElement = document.createElement("li"); taskElement.classList.add("flex", "items-center", "space-x-2"); //Add Bullet Point Element const bullet = document.createElement("span"); bullet.classList.add("h-2", "w-2", "bg-blue-500", "rounded-full"); //Add Span Tag const taskText = document.createElement("span"); taskText.textContent = task; taskElement.appendChild(bullet); taskElement.appendChild(taskText); lists.appendChild(taskElement); }) }
const init = () => { console.log("Initializing..."); //Update and render the list updateList(); //Initialize Fuse with the updated array try{ fuse = new Fuse(tasks, { includeScore: true, threshold: 0.3 //For sensitivity }) } catch(e) { console.log("Error initializing Fuse:"+ fuse); } }
document.addEventListener("DOMContentLoaded", init);
到目前为止:每次您输入内容时,下拉列表都会更新 - 在更庞大的 UI 中,我们不希望出现这种体验
在庞大的 UI 中每次击键时更新下拉列表可能会导致性能问题,从而导致延迟和糟糕的用户体验。频繁的更新可能会压垮事件循环,导致处理其他任务的延迟。
我们现在将了解如何使用去抖或节流来帮助管理更新频率,确保更流畅的性能和更灵敏的界面。
以下是我们如何在笔记制作项目中实现这两种技术的方法。
去抖动确保函数仅在自上次调用以来经过指定时间后才被调用。这对于搜索输入字段等场景非常有用,在这些场景中我们希望等待用户完成输入后再进行 API 调用。
代码片段:
//Utility function to search within already entered values const searchTasks = (query) => { const result = fuse.search(query); const filteredTasks = result.map(result => result.item) updateDropdown(filteredTasks); }
说明:
const updateDropdown = (tasks) => { const dropdown = document.getElementById("dropdown"); dropdown.innerHTML = ""; if(tasks.length === 0) { dropdown.style.display = "none"; return; } tasks.forEach(task => { const listItem = document.createElement("li"); listItem.textContent = task; listItem.addEventListener("click", () => { document.getElementById("input").value = task; dropdown.style.display = "none"; }) dropdown.appendChild(listItem); }); dropdown.style.display = "block"; }
说明:
但是,请注意:限流并不是最适合这种场景,因为它将函数执行的频率限制在固定的时间间隔内,这可能无法为实时搜索建议提供最佳的用户体验。用户期望在输入时立即获得反馈,而限制可能会导致明显的延迟。
限流更适合您想要控制事件处理速率以避免性能问题的场景。以下是一些示例:
通过在这些场景中使用限制,您可以提高性能并确保更流畅的用户体验。
在此处查找完整代码
编码快乐!
请留下反馈!
我希望您觉得这个博客有帮助!您的反馈对我来说非常宝贵,所以请在下面的评论中留下您的想法和建议。
请随时在 LinkedIn 上与我联系以获取更多见解和更新。让我们保持联系,继续共同学习和成长!
以上是平滑用户体验的艺术:去抖动和节流以获得更高性能的用户界面的详细内容。更多信息请关注PHP中文网其他相关文章!