注意:该帖子最初发表在my blog
上静态站点发电机(SSG),例如Astro,非常适合构建静态页面。但是,它们很少包含网站搜索功能。通常,没有内容管理系统(CMS)或数据库可容纳内容,并且您可以查询搜索词。
在本教程中,我们将对Astro进行简单的搜索,使我们可以查询Markdown/MDX帖子的前磨。我们将创建一个执行搜索的先性挂钩。对于UI,我们将创建一个提前组件,以使用户输入搜索术语以及一个preact Component以在列表中显示搜索结果。
工具
在命令行上,我一直在使用fzf与fd结合使用了一段时间。我用它来搜索目录,外壳历史记录中的先前命令以及其他搜索/过滤器相关的任务。幸运的是,还有FZF for JavaScript,它是浏览器FZF的非正式港口。我们将使用此模块为搜索供电。
对于用户交互,我们需要一些HTML和客户端JavaScript。为了使我们变得更容易,我们将使用Astro's Preact integration,如果您还没有这样做,则需要setup。
安装FZF
要将FZF添加到我们的Astro项目中,我们需要将其添加为package.json
中的依赖性:
{
"dependencies": {
"fzf": "0.5.1"
}
}
之后,我们使用纱线安装它 - 或您选择的任何包装管理器:
yarn install
# If you use npm, use this command instead
npm install
useFuzzySearch
钩
要执行实际搜索,我们创建了一个挂钩,该挂钩可以通过我们的Markdown/MDX帖子的前牙字段进行搜索。我们记住搜索结果以免一遍又一遍地执行相同的搜索,但仅在我们想要搜索更改的帖子,搜索词或前磨牙字段时才搜索。
import { useMemo } from 'preact/hooks';
import { Fzf } from 'fzf';
/**
* Search for search term in post's frontmatter
*
* @param {array} posts List of posts to search in
* @param {string} searchTerm Term to search for
* @param {string} field Frontmatter item in which to search
* @param {object} [fzfOptions={ }] Additional options to pass to fzf
*/
function useFuzzySearch(posts, searchTerm, field, fzfOptions = { }) {
const fzf = useMemo(() => {
return new Fzf(posts, {
selector: post => post.frontmatter[field],
...fzfOptions
}, [ posts, field, fzfOptions]);
});
const searchResults = useMemo(() => {
return fzf.find(searchTerm);
}, [ fzf, searchTerm ]);
return searchResults;
}
用户界面
对于本教程,我们将保持UI非常简单。我们需要一个具有输入元素的组件,供用户输入搜索词以及显示实际搜索结果的组件。
注意:我故意遗漏了所有CSS样式,错误处理等,以使理解本教程尽可能容易。
Search
组件
搜索组件期望 - 作为prop-将要搜索前材的帖子,以及应搜索的字段。每当searchTerm
更改时,都会执行useFuzzySearch
钩以检索搜索结果。如果有搜索结果,则将这些结果传递给我们将在下一步中创建的SearchResults
组件。
import { useState } from 'preact/hooks';
import useFuzzySearch from './useFuzzySearch';
import SearchResults from './SearchResults';
const DEFAULT_SEARCH_TERM = '';
function Search(props) {
// Initialize state variables
const [ searchTerm, setSearchTerm ] = useState(DEFAULT_SEARCH_TERM);
const searchResults = useFuzzySearch(
props.posts,
searchTerm,
props.field,
{ fuzzy: false } // We only search for full word matches. See
// https://fzf.netlify.app/docs/latest#api
// for available options
);
// Called whenever the value of the input field changes.
function onInput(e) {
const searchTerm = e?.target?.value;
if (searchTerm && searchTerm.length > 0) {
setSearchTerm(searchTerm);
} else {
setSearchTerm(DEFAULT_SEARCH_TERM);
}
}
return (
<div>
<input
onInput={ onInput }
type="text"
/>
{
searchResults &&
<SearchResults
searchResults={ searchResults }
/>
}
</div>
)
}
SearchResults
组件
SearchResults
组件将显示一个简单的列表,其中帖子的前后字段与searchTerm
匹配。
function SearchResults(props) {
<div>
{
props.noOfSearchResults === 0 &&
<span>No search results</span>
}
{
props.noOfSearchResults > 0 &&
props.searchResults.map(result => (
<p>
{ result.item.frontmatter.title }
</p>
))
}
</div>
}
如何使用
现在,我们已经准备好了搜索的逻辑和UI,我们需要做的就是将Search
组件添加到任何Astro页面。我们添加client:visible
directive,一旦用户可见搜索后,我们都可以补充我们的预性组件。
---
import Search from `Search.jsx`;
const posts = await Astro.glob('@posts/**/*.mdx');
---
<Search
field="title"
posts={ posts }
client:visible
/>
最后一句话
这只是如何将搜索功能添加到Astro供电的网站上的一个基本示例。如前所述,我忽略了所有CSS样式,错误处理等,以使本教程易于理解。但是,如果您想自己在网站上实施搜索,那应该是一个不错的起点。
添加了一些CSS,最终结果可能看起来像下面的图像。要进行互动演示,请转到我博客的archive page。