Fuse.js是一个轻量级的搜索引擎,能够在用户的浏览器中的客户端运行。让咱们看看如何使用它来轻松地为React应用添加搜索功能。html
搜索功能对不少类型的网站都颇有用,可让用户高效地找到他们想要的东西。但为何咱们会专门选择使用Fuse.js呢?node
为搜索提供动力的选择有不少,最简单的多是使用现有的数据库。例如,Postgres有一个全文搜索功能。MySQL也有,Redis也有RediSearch模块。react
还有一些专门的搜索引擎,其中Elasticsearch和Solr是最受欢迎的。这些搜索引擎须要更多的设置,但它们拥有你的用例可能须要的高级功能。git
最后,你可使用Algolia或Swiftype等搜索即服务平台。这些服务运行本身的搜索基础架构。你只需经过API提供数据、配置和查询。github
可是,您可能不须要这些解决方案所暴露的能力,这可能须要大量的工做来实现,更不用说成本了。若是没有太多的数据须要搜索,Fuse.js须要最小的设置,而且能够提供比您本身可能想到的更好的搜索体验。shell
至于多少数据对Fuse.js来讲是过多的,考虑到Fuse.js须要访问整个数据集,因此你须要在客户端所有加载。若是数据集的大小是100MB,那就超出了发送给客户端的合理范围。但若是它只有几千字节,它多是Fuse.js的一个很好的候选者。数据库
让咱们作一个基本的React应用,使用Fuse.js让用户搜索狗的品种。你能够在这里查看最终的结果,源代码能够在GitHub上找到。npm
咱们将从设置一些脚手架开始。从一个新的Node.js项目开始,咱们将安装React和Fuse.js:json
npm install --save react react-dom fuse.js //or yarn add react react-dom fuse.js
咱们还将安装Parcel做为开发依赖项:swift
npm install --save-dev parcel@2.0.0-beta.1 //or yarn add --dev parcel@2.0.0-beta.1
咱们将在 package.json
启动脚本中使用它来编译应用程序:
{ "scripts": { "start": "parcel serve ./index.html --open" } }
接下来,咱们将建立一个barebones index.html
,其中包含一个空的 div
供React渲染,以及一个 noscript
消息,以免在用户禁用JavaScript时出现空白页面。
<!DOCTYPE html> <html lang="en"> <body> <div id="app"></div> <noscript> <p>Please enable JavaScript to view this page.</p> </noscript> <script src="./index.js"></script> </body> </html>
咱们将使咱们的 index.js
简单的开始。咱们将渲染一个有搜索查询输入的表单,尽管咱们还不会实际处理搜索。
import React, { useState } from "react"; import ReactDom from "react-dom"; function Search() { return ( <form> <label htmlFor="query">Search for a dog breed:</label> <input type="search" id="query" /> <button>Search</button> </form> ); } ReactDom.render(<Search />, document.getElementById("app"));
此时,若是你运行 npm run start
或 yarn run start
,Parcel应该会在浏览器中打开网站,你应该会看到这个表单。
如今开始实施搜索,咱们将从显示搜索结果的组件开始。咱们须要处理三种状况:
咱们将在ordered list中显示全部结果。
function SearchResults(props) { if (!props.results) { return null; } if (!props.results.length) { return <p>There are no results for your query.</p>; } return ( <ol> {props.results.map((result) => ( <li key={result}>{result}</li> ))} </ol> ); }
咱们也来编写本身的搜索函数。稍后,咱们将可以将咱们的简单方法的结果与Fuse.js的结果进行比较。
咱们的方法很简单:咱们将遍历犬种数组(来自这个JSON列表),并返回包含整个搜索查询的全部犬种。咱们还会让全部东西都小写,这样搜索就不区分大小写了。
const dogs = [ "Affenpinscher", "Afghan Hound", "Aidi", "Airedale Terrier", "Akbash Dog", "Akita", // More breeds.. ]; function searchWithBasicApproach(query) { if (!query) { return []; } return dogs.filter((dog) => dog.toLowerCase().includes(query.toLowerCase())); }
接下来,让咱们将全部内容连接在一块儿,方法是从表单提交中获取搜索查询,而后执行搜索并显示结果。
function Search() { const [searchResults, setSearchResults] = useState(null); return ( <> <form onSubmit={(event) => { event.preventDefault(); const query = event.target.elements.query.value; const results = searchWithBasicApproach(query); setSearchResults(results); }} > <label htmlFor="query">Search for a dog breed:</label> <input type="search" id="query" /> <button>Search</button> </form> <SearchResults results={searchResults} /> </> ); }
使用Fuse.js很简单,咱们须要导入它,让它使用 new Fuse()
对数据进行索引,而后使用索引的搜索功能。搜索会返回一些元数据,因此咱们将只提取实际的项目进行展现。
import Fuse from "fuse.js"; const fuse = new Fuse(dogs); function searchWithFuse(query) { if (!query) { return []; } return fuse.search(query).map((result) => result.item); }
元数据包括一个 refIndex
整数,让咱们能够回溯到原始数据集中的相应项目。若是咱们用 new Fuse(dogs, {includeScore: true})
初始化索引,咱们也会获得匹配分数:一个介于0和1之间的值,其中0是彻底匹配。那么“Husky”的搜索结果就会像这样:
[ { item: "Siberian Husky", refIndex: 386, score: 0.18224241177399383 } ]
咱们将在 Search
组件的表单中添加一个复选框,让用户选择是否使用Fuse.js而不是基本的搜索函数。
<form onSubmit={(event) => { event.preventDefault(); const query = event.target.elements.query.value; const useFuse = event.target.elements.fuse.checked; setSearchResults( useFuse ? searchWithFuse(query) : searchWithBasicApproach(query) ); }} > <label htmlFor="query">Search for a dog breed: </label> <input type="search" id="query" /> <input type="checkbox" name="fuse" /> <label htmlFor="fuse"> Use Fuse.js</label> <button>Search</button> </form>
如今咱们能够用Fuse.js进行搜索了!咱们可使用复选框来比较使用它和不使用它。
最大的区别在于Fuse.js能够容忍错别字(经过近似字符串匹配),而咱们的基本搜索则须要精确匹配。若是咱们把“retriever”拼错为“retreiver”,请查看基本搜索结果。
如下是针对同一查询更有用的Fuse.js结果:
若是咱们关心多个字段,咱们的搜索可能会更复杂。例如,想象一下,咱们想经过品种和原产国来搜索。Fuse.js支持这种用例。当咱们建立索引时,咱们能够指定要索引的对象键。
const dogs = [ {breed: "Affenpinscher", origin: "Germany"}, {breed: "Afghan Hound", origin: "Afghanistan"}, // More breeds.. ]; const fuse = new Fuse(dogs, {keys: ["breed", "origin"]});
如今,Fuse.js将同时搜索 breed
和 origin
字段。
有时候,咱们不想花费资源去创建一个完整的Elasticsearch实例。当咱们有简单的需求时,Fuse.js能够提供相应的简单解决方案。而正如咱们所看到的,将它与React一块儿使用也是很简单的。
即便咱们须要更高级的功能,Fuse.js也容许给不一样的字段赋予不一样的权重,添加 AND
和 OR
逻辑,调整模糊匹配逻辑等等。当你下次须要在应用中添加搜索功能时,能够考虑使用它。