摘要:在本教程中,您将了解 JavaScript 防抖函数及其如何用于改进应用程序性能。
为了理解防抖函数,我们将使用防抖编程技术构建一个维基百科搜索应用程序。
创建项目文件夹结构
首先,创建一个名为 wikipedia-search
的新文件夹,用于存储项目的文件夹。
其次,在 wikipedia-search
文件夹中创建三个文件夹,分别命名为 js
、css
和 img
。这些文件夹将分别存储 JavaScript、CSS 和图像文件。
第三,在 css
文件夹中创建 style.css
,在 js
文件夹中创建 app.js
。此外,下载以下图像并将其复制到 img
文件夹中。您将使用该徽标来制作应用程序的 UI。

最后,在根文件夹中创建一个 index.html
文件。
项目结构将如下所示

构建 HTML 页面
打开 index.html 文件并添加以下代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wikipedia Search</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<header>
<img src="./img/wikipedia-logo.png" alt="wikipedia">
<h1>Wikipedia Search</h1>
<input type="text" name="searchTerm" id="searchTerm" placeholder="Enter a search term...">
</header>
<main id="searchResult"></main>
<script src="js/app.js"></script>
</body>
</html>
Code language: HTML, XML (xml)
在此 HTML 文件中
- 首先,在
<head>
部分中链接到style.css
文件。 - 其次,添加一个
<script>
标签,其src
链接到app.js
文件,并将其放置在</body>
标签之前。 - 第三,在 HTML 页面的主体中添加两个部分。第一部分是页眉,显示维基百科徽标、标题和搜索框。第二部分包含
<main>
标签,用于显示搜索结果。
复制 CSS 代码
请导航到 style.css 文件,复制其代码,并将代码粘贴到 css
文件夹中的 style.css
文件中。当您打开 index.html
文件时,您应该看到类似于 以下页面的内容。
处理输入事件
首先,使用 querySelector()
方法选择 <input>
和搜索结果元素
const searchTermElem = document.querySelector('#searchTerm');
const searchResultElem = document.querySelector('#searchResult');
Code language: JavaScript (javascript)
其次,通过调用 focus()
方法将焦点设置在 <input>
元素上
searchTermElem.focus();
Code language: JavaScript (javascript)
第三,为 <input>
元素附加一个 input
事件监听器
searchTermElem.addEventListener('input', function (event) {
console.log(event.target.value);
});
Code language: JavaScript (javascript)
如果您在 <input>
元素中键入一些文本,您将看到 input
事件发生,这将向控制台显示文本。
例如,当您在 <input>
元素中键入 debounce
时

… 您将在控制台中看到以下文本

使用维基百科 API 获取搜索结果
维基百科 API 非常简单。它不需要 API 密钥。
要通过搜索词获取主题,您需要将 srsearch
查询参数附加
&srsearch=<searchTerm>
Code language: JavaScript (javascript)
到以下 URL
https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10
Code language: JavaScript (javascript)
… 并发送 HTTP GET
请求。
例如,您可以通过向以下 URL 发送 HTTP GET
请求来获取与 debounce
关键字相关的主题
https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10&srsearch=debounce
Code language: JavaScript (javascript)
顺便说一下,您可以在 Web 浏览器中打开上述 URL 以查看响应。
从 JavaScript 中,您可以使用 fetch API(所有现代 Web 浏览器中都可用)来发送 HTTP GET
请求。
以下代码创建了一个 search()
函数,该函数接受一个搜索词,向维基百科发出 HTTP GET
请求,并将搜索结果显示到控制台中
const search = async (searchTerm) => {
try {
const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10&srsearch=${searchTerm}`;
const response = await fetch(url);
const searchResults = await response.json();
// show the search result in the console
console.log({
'term': searchTerm,
'results': searchResults.query.search
});
} catch (error) {
console.log(error);
}
}
Code language: JavaScript (javascript)
工作原理。
首先,通过将 srsearch
查询参数添加到端点来构造 API URL
const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10&srsearch=${searchTerm}`;
Code language: JavaScript (javascript)
其次,使用 fetch()
方法发送 HTTP GET 请求。由于 fetch() 方法返回一个 promise,因此您需要使用 await
关键字来等待响应。
fetch()
函数返回的 promise 有许多方法,其中一个是 json()
。json()
方法还返回另一个 promise,该 promise 解析为 JSON 格式的结果。
由于 await
关键字的存在,您需要将 search()
函数标记为 async
函数,如下所示
const search = async (searchTerm) = {
/// ...
};
Code language: JavaScript (javascript)
json()
方法返回的对象具有许多属性。为了获取搜索结果,您需要访问 searchResults.query.search
属性。
要测试 search()
方法,您可以像下面这样在 input
事件监听器中调用它
searchTermElem.addEventListener('input', function (event) {
search(event.target.value);
});
Code language: JavaScript (javascript)
以下是完整的 app.js
文件
const searchTermElem = document.querySelector('#searchTerm');
const searchResultElem = document.querySelector('#searchResult');
searchTermElem.select();
searchTermElem.addEventListener('input', function (event) {
search(event.target.value);
});
const search = async (searchTerm) => {
try {
const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10&srsearch=${searchTerm}`;
const response = await fetch(url);
const searchResults = await response.json();
// show the search result in the console
console.log({
'term': searchTerm,
'results': searchResults.query.search
});
} catch (error) {
console.log(error);
}
}
Code language: JavaScript (javascript)
现在,如果您在 Web 浏览器中打开 index.html
文件并在输入元素中键入 debounce
关键字,您将在控制台中看到以下结果

输出表明,search()
函数会针对您键入的每个字符执行。它会针对每个文本输入调用 API,这效率不高。
为了限制请求数量,您将只在必要时发送 API 请求。换句话说,您将仅在用户暂停或停止键入一段时间(例如,半秒钟)后发送 API 请求。
为此,您可以使用 setTimeout() 和 clearTimeout()
函数
- 当用户键入一个字符时,使用
setTimeout()
函数来安排在一段时间后执行 search() 函数。 - 如果用户继续键入,则使用
clearTimeout()
函数取消该计时器。如果用户暂停或停止键入,则让计时器执行已安排的搜索函数。
以下是 search()
函数的新版本
let timeoutId;
const search = (searchTerm) => {
// reset the previous timer
if (timeoutId) {
clearTimeout(timeoutId);
}
// set up a new timer
timeoutId = setTimeout(async () => {
try {
const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10&srsearch=${searchTerm}`;
const response = await fetch(url);
const searchResults = await response.json();
// show the search result in the console
console.log({
'term': searchTerm,
'results': searchResults.query.search
});
} catch (error) {
console.log(error);
}
}, 500);
};
Code language: JavaScript (javascript)
由于 await
相关的代码已移至 setTimeout()
的回调函数中,因此您需要用 async
关键字标记回调函数,并从 search()
函数中删除 async
关键字。
如果您在 Web 浏览器中打开 index.html
文件并在输入关键字 debounce 时不暂停(半秒钟)然后停止,您将看到应用程序只会发出一个 API 请求。
这种技术被称为防抖。
什么是防抖
如果您有一个耗时的任务,例如 API 请求,该请求经常被触发,它会影响应用程序性能。
防抖是一种编程技术,它限制函数被调用的次数。
开发一个可重用的防抖函数
debounce()
函数需要接受一个函数 (fn
),限制对它的调用次数,并返回一个函数
const debounce = (fn) => {
return (arg) => {
// logic to limit the number of call fn
fn(arg);
};
};
Code language: JavaScript (javascript)
以下代码使用 clearTimeout()
和 setTimeout()
函数来防抖 fn
函数
const debounce = (fn) => {
let timeoutId;
return (arg) => {
// cancel the previous timer
if (timeoutId) {
clearTimeout(timeoutId);
}
// setup a new timer
timeoutId = setTimeout(() => {
fn(arg);
}, 500);
};
};
Code language: JavaScript (javascript)
通常,fn
函数将接受多个参数。要使用参数列表调用 fn
函数,您需要使用 apply()
方法
const debounce = (fn, delay=500) => {
let timeoutId;
return (...args) => {
// cancel the previous timer
if (timeoutId) {
clearTimeout(timeoutId);
}
// setup a new timer
timeoutId = setTimeout(() => {
fn.apply(null, args);
}, delay);
};
};
Code language: JavaScript (javascript)
工作原理
- 首先,用
delay
参数替换硬编码的数字500
,以便您可以指定在执行fn
函数之前等待的时间。延迟的默认值为 500 毫秒。 - 其次,在返回的函数中添加
...args
。...arg
是一个 剩余参数,它允许您将fn()
函数的所有参数收集到数组args
中。 - 第三,
fn.apply(null, args)
使用args
数组中指定的参数执行fn()
函数。
使用防抖函数
以下代码从 search()
函数中删除了防抖逻辑,而是使用 debounce()
函数
const search = debounce(async (searchTerm) => {
try {
const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10&srsearch=${searchTerm}`;
const response = await fetch(url);
const searchResults = await response.json();
// show the search result in the console
console.log({
'term': searchTerm,
'results': searchResults.query.search
});
} catch (error) {
console.log(error);
}
});
Code language: JavaScript (javascript)
将搜索结果转换为 HTML
我们将在输出中显示每个搜索结果的标题和摘要。在这样做之前,我们需要一些实用程序函数
去除 HTML 标签
API 调用返回的搜索结果中的 title
和 snippet
可能包含 HTML 标签。在渲染它们之前,安全起见,需要去除所有 HTML 标签。
以下实用程序函数从字符串中去除 HTML 标签
const stripHtml = (html) => {
let div = document.createElement('div');
div.html = html;
return div.textContent;
};
Code language: JavaScript (javascript)
stripHtml()
函数接受一个 HTML 字符串。它创建一个临时 <div>
元素,将其 innerHTML
设置为 HTML 字符串,并返回其 textContent
属性。
请注意,此函数仅在 Web 浏览器中有效,因为它依赖于 Web 浏览器的 DOM API。
突出显示搜索词
如果搜索词在搜索结果中被突出显示,则会更直观。
此 highlight()
函数通过用带有 highlight
类别的 <span>
标签包装 str
中 keyword
的所有出现来突出显示 keyword
的所有出现
const highlight = (str, keyword, className = "highlight") => {
const hl = `<span class="${className}">${keyword}</span>`;
return str.replace(new RegExp(keyword, 'gi'), hl);
};
Code language: JavaScript (javascript)
请注意,该函数使用 正则表达式 来用 <span>
元素替换 keyword
的所有出现。
将搜索结果转换为 HTML
以下 generateSearchResultHTML()
函数将搜索结果转换为 HTML
const generateHTML= (results, searchTerm) => {
return results
.map(result => {
const title = highlight(stripHtml(result.title), searchTerm);
const snippet = highlight(stripHtml(result.snippet), searchTerm);
return `<article>
<a href="https://en.wikipedia.org/?curid=${result.pageid}">
<h2>${title}</h2>
</a>
<div class="summary">${snippet}...</div>
</article>`;
})
.join('');
}
Code language: JavaScript (javascript)
工作原理。
- 首先,使用
map()
方法返回每个搜索结果的 HTML 表示形式,并使用join()
方法将搜索结果(HTML 格式)连接成一个 HTML 字符串。 - 其次,去除 API 调用返回的
title
和snippet
中的 HTML 标签,并突出显示搜索词。
显示搜索结果
更改使用 generateSearchResultHTML()
函数的 search()
方法,并将结果追加到 searchResultElem
。此外,如果搜索词为空,则重置搜索结果
const search = debounce(async (searchTerm) => {
// if the search term is removed,
// reset the search result
if (!searchTerm) {
// reset the search result
searchResultElem.innerHTML = '';
return;
}
try {
// make an API request
const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10&srsearch=${searchTerm}`;
const response = await fetch(url);
const searchResults = await response.json();
// render search result
const searchResultHtml = generateSearchResultHTML(searchResults.query.search, searchTerm);
// add the search result to the searchResultElem
searchResultElem.innerHTML = searchResultHtml;
} catch (error) {
console.log(error);
}
});
Code language: JavaScript (javascript)
现在,如果您在 Web 浏览器中打开 index.html
,您将看到 工作应用程序。
总结
在本教程中,您学习了以下关键要点
- 使用
fetch()
API 发出 HTTPGET
请求。 - 使用
async/await
关键字使异步代码更简洁。 - 了解防抖编程技术并开发一个可重用的 JavaScript
debounce()
函数。