typeahead.js 完整指南:基于 jQuery 的高性能自动补全库与 Bloodhound 建议引擎

typeahead.js 完整指南:基于 jQuery 的高性能自动补全库与 Bloodhound 建议引擎

【免费下载链接】typeahead.js typeahead.js is a fast and fully-featured autocomplete library 【免费下载链接】typeahead.js 项目地址: https://gitcode.com/gh_mirrors/ty/typeahead.js

导读

typeahead.js 是一款受 Twitter 搜索自动补全功能启发而诞生的 JavaScript 自动补全(autocomplete)库。它由建议引擎 Bloodhound 与 UI 视图 Typeahead 两个组件构成,前者负责计算匹配建议,后者负责渲染建议与处理 DOM 交互;两者既可独立使用,也可组合成完整的 typeahead 体验。本文将以仓库 README 为主线,结合源码与官方文档,完整讲解安装方式、两大组件的 API 与全部配置项、浏览器支持、版本策略、测试与开发工作流,帮助你在实际项目中快速集成并深度定制自动补全能力。

一、项目概览:两个组件的架构设计

README 明确指出,typeahead.js 由两个核心组件构成:

  • Bloodhound:建议引擎,负责为给定查询计算建议。支持硬编码数据、初始化时预取(prefetch)、智能缓存、快速查找,以及用远程数据回填(backfill)不足的结果。
  • Typeahead:UI 视图,以 jQuery 插件形式提供,负责渲染建议、展示提示(hint)并处理全部 DOM 交互。

两者可以单独使用,但组合使用能提供最丰富的体验。从源码结构看,这一分层非常清晰:

在 Gruntfile.js 的构建文件清单中,bloodhound 与 typeahead 两组源文件被分别合并,这印证了两组件的独立性:bloodhound 组产出独立的 bloodhound.js,typeahead 组产出独立的 typeahead.jquery.js,二者合并后即为 typeahead.bundle.js。

二、获取与安装

README 提供了多种获取方式,按偏好程度排列:

首选方式:Bower 安装

$ bower install typeahead.js

其他方式:

  • 下载最新版本的 zip 包;
  • 单独下载最新的 dist 产物文件:
    • bloodhound.js —— 独立建议引擎;
    • typeahead.jquery.js —— 独立 UI 视图;
    • typeahead.bundle.js —— bloodhound.js 与 typeahead.jquery.js 的合并包(最常用);
    • typeahead.bundle.min.js —— 上述合并包的压缩版。

从 package.json 可以看出,main 字段指向 dist/typeahead.bundle.js,说明合并包是 npm 环境下的标准入口;name 与 description("fast and fully-featured autocomplete library")也与 README 的项目定位一致。

重要依赖说明:无论 bloodhound.js 还是 typeahead.jquery.js,都依赖 jQuery 1.9+(README 声明;package.json 的 dependencies 中声明为 "jquery": ">=1.7",实际使用请以 1.9+ 为准)。集成时务必在引入 typeahead.js 之前引入满足版本要求的 jQuery。

三、Bloodhound 建议引擎详解

Bloodhound 是 typeahead.js 的"大脑",对应的完整官方文档见 doc/bloodhound.md。它的核心能力包括:

  • 支持硬编码数据(local);
  • 初始化时预取数据(prefetch),降低建议延迟;
  • 智能利用本地存储,减少网络请求;
  • 从远程源回填建议(remote);
  • 对远程请求做限流(rate-limit)与缓存,减轻服务端负载。

3.1 基础用法与完整 API

创建一个引擎并传入一个 options 哈希即可:

var engine = new Bloodhound({
  local: ['dog', 'pig', 'moose'],
  queryTokenizer: Bloodhound.tokenizers.whitespace,
  datumTokenizer: Bloodhound.tokenizers.whitespace
});

Bloodhound 暴露了以下实例方法:

Bloodhound.noConflict() —— 返回 Bloodhound 引用并将 window.Bloodhound 还原为旧值,用于避免命名冲突:

var Dachshund = Bloodhound.noConflict();

Bloodhound#initialize(reinitialize) —— 启动引擎初始化。初始化过程会把 local 与 prefetch 提供的数据加入内部搜索索引,并建立 remote 使用的传输机制。在调用 #initialize 之前,#get 与 #search 实际上是空操作。除非将 initialize 选项设为 false,构造函数会隐式调用它:

var engine = new Bloodhound({
  initialize: false,
  local: ['dog', 'pig', 'moose'],
  queryTokenizer: Bloodhound.tokenizers.whitespace,
  datumTokenizer: Bloodhound.tokenizers.whitespace
});

var promise = engine.initialize();

promise
.done(function() { console.log('ready to go!'); })
.fail(function() { console.log('err, something went wrong :('); });

初始化返回 jQuery Promise。后续再次调用 #initialize 时:若 reinitialize 为假值,则不重复执行初始化逻辑,直接返回首次调用得到的同一个 Promise;若为真值,则如同首次调用一样重新初始化。

Bloodhound#add(data) —— 将数组数据加入内部搜索索引:

engine.add([{ val: 'one' }, { val: 'two' }]);

Bloodhound#get(ids) —— 返回搜索索引中对应 ids 的数据:

var engine = new Bloodhound({
  local: [{ id: 1, name: 'dog' }, { id: 2, name: 'pig' }],
  identify: function(obj) { return obj.id; },
  queryTokenizer: Bloodhound.tokenizers.whitespace,
  datumTokenizer: Bloodhound.tokenizers.whitespace
});

engine.get([1, 3]); // [{ id: 1, name: 'dog' }, null]

Bloodhound#search(query, sync, async) —— 返回匹配 query 的数据。本地搜索索引中的匹配结果传给 sync 回调;若传给 sync 的数据不足 sufficient 条,会请求 remote 数据并传给 async 回调:

engine.search(myQuery, sync, async);

function sync(datums) {
  console.log('datums from `local`, `prefetch`, and `#add`');
  console.log(datums);
}

function async(datums) {
  console.log('datums from `remote`');
  console.log(datums);
}

Bloodhound#clear() —— 清空由 local、prefetch 与 #add 填充的内部搜索索引:

engine.clear();

此外,从 bloodhound.js 源码还可以看到两个缓存清理方法:clearPrefetchCache()(清空 prefetch 本地存储缓存)与 clearRemoteCache()(调用 Transport.resetCache() 重置传输层缓存),以及供 jQuery 插件内部集成的 __ttAdapter() 方法(返回适配器函数,将 search(query, sync, async) 包装为数据集 source 的签名);ttAdapter() 是它的废弃别名。

3.2 Options 配置项

实例化 Bloodhound 时可配置的选项如下:

  • datumTokenizer —— 签名为 (datum) 的函数,将一条 datum 转换为字符串 token 数组。必填。
  • queryTokenizer —— 签名为 (query) 的函数,将查询转换为字符串 token 数组。必填。
  • initialize —— 若为 false,构造器不会隐式初始化。默认 true。
  • identify —— 给定一条 datum,返回其唯一 id 的函数。默认 JSON.stringify。强烈建议覆盖此选项(否则对象字段顺序或引用变化都会影响去重与索引)。
  • sufficient —— 当内部搜索索引提供的数据条数小于该值时,#search 会触发 remote 回填。默认 5。
  • sorter —— 用于对内部搜索索引返回数据进行排序的比较函数。
  • local —— 数据数组或返回数据数组的函数。#initialize 时加入内部搜索索引。
  • prefetch —— 指向包含数据数组的 JSON 文件的 URL,或一个 prefetch 选项哈希。
  • remote —— 当内部数据不足时用于取数的 URL,或一个 remote 选项哈希。

3.3 Prefetch:预取与本地存储缓存

Prefetch 数据在初始化时被获取并处理。若浏览器支持本地存储,处理后的数据会缓存在本地存储中,从而在后续页面加载时避免额外的网络请求。

警告:尽管小数据集可以这么用,prefetch 数据不应包含完整数据集,而应作为第一级缓存(first-level cache)。无视此警告可能触及本地存储容量上限。

配置 prefetch 可用以下选项:

  • url —— 预取数据加载的 URL。必填。
  • cache —— 若为 false,不读写本地存储,初始化时总是从 url 加载。默认 true。
  • ttl —— 预取数据在本地存储中的缓存时长(毫秒)。默认 86400000(1 天)。
  • cacheKey —— 数据在本地存储中的键名。默认取 url 的值。
  • thumbprint —— 用于"指纹"校验预取数据的字符串。若与本地存储中的不一致,会重新获取数据。
  • prepare —— 请求即将发出前,允许你调整传给 transport 的 settings 对象的钩子。签名 prepare(settings),应返回 settings 对象。默认为恒等函数。
  • transform —— 签名 transform(response),允许你在 Bloodhound 处理响应前转换预取响应。默认为恒等函数。

从源码看,_loadPrefetch 的流程是:若未配置 prefetch 直接 resolve;否则先尝试 prefetch.fromCache() 读取本地缓存,命中则用 this.index.bootstrap(serialized) 直接灌入索引;未命中则 prefetch.fromNetwork(done) 从网络加载,成功后 this.add(data) 并将 this.index.serialize() 序列化结果通过 store 写回缓存(见 bloodhound.js)。

3.4 Remote:限流、回填与去重

Bloodhound 只有在内部搜索引擎无法提供足够结果时才访问网络。为防止对远端接口发出过量请求,远程请求会做限流。

配置 remote 可用以下选项:

  • url —— 远程数据加载的 URL。必填。
  • prepare —— 请求即将发出前的钩子。签名 prepare(query, settings),其中 query 是 #search 调用时的查询词,settings 是 Bloodhound 内部创建的默认 settings 对象,函数应返回 settings 对象。默认为恒等函数。
  • wildcard —— prepare 的便捷选项。若设置,prepare 会被替换为:将 url 中的该占位符替换为 URI 编码后的查询词。
  • rateLimitBy —— 限流方式,debounce 或 throttle。默认 debounce。
  • rateLimitWait —— 限流的时间间隔(毫秒)。默认 300。
  • transform —— 签名 transform(response),允许你在 Bloodhound 处理前转换远程响应。默认为恒等函数。

从 bloodhound.js 源码可以确认 search 的完整流程:先对本地索引结果排序并同步回调;若配置了 remote 且本地结果少于 sufficient,则调用 this.remote.get(query, processRemote) 异步拉取;否则取消上次未发出的限流请求(注释 #149 说明这是为了防止过期的限流请求被发出)。远程结果返回后,会通过 identify 与本地结果做去重,剔除与本地重复的条目后交给 async 回调。

四、jQuery#typeahead UI 组件详解

Typeahead 的 UI 组件以 jQuery 插件形式提供,负责渲染建议与处理 DOM 交互,完整官方文档见 doc/jquery_typeahead.md。其特性包括:

  • 用户输入时实时展示建议;
  • 将顶部建议显示为 hint(背景提示文本);
  • 支持自定义模板,UI 灵活;
  • 对 RTL 语言与输入法(IME)支持良好;
  • 在建议中高亮查询匹配;
  • 触发自定义事件,便于扩展。

4.1 初始化与完整 API

jQuery#typeahead(options, [*datasets]) —— 为给定的 input[type="text"] 启用 typeahead 功能。options 是配置哈希,其后的若干参数是各数据集的配置哈希:

$('.typeahead').typeahead({
  minLength: 3,
  highlight: true
},
{
  name: 'my-dataset',
  source: mySource
});

从 plugin.js 源码看,initialize 同时支持两种签名:function(o, dataset, dataset, ...) 与 function(o, [dataset, dataset, ...])(数组会被 _.isArray 识别)。初始化时会为每个 input 元素创建 hint 输入框、menu 菜单、EventBus、Input、Menu 与 Typeahead 实例,并将 highlight 顶层配置继承到所有数据集(_.each(datasets, function(d) { d.highlight = !!o.highlight; }))。容器与输入框会被包装并加上 tt- 系列 class,提示框为空时自动创建。

jQuery#typeahead('val') —— 返回 typeahead 当前值(即用户输入到 input 元素中的文本):

var myVal = $('.typeahead').typeahead('val');

jQuery#typeahead('val', val) —— 设置 typeahead 的值。官方建议用它替代 jQuery#val:

$('.typeahead').typeahead('val', myVal);

jQuery#typeahead('open') / ('close') —— 打开 / 关闭建议菜单:

$('.typeahead').typeahead('open');
$('.typeahead').typeahead('close');

jQuery#typeahead('destroy') —— 移除 typeahead 功能,将 input 元素还原为初始状态:

$('.typeahead').typeahead('destroy');

从源码看,destroy 会调用 revert($input) 恢复被修改的属性(autocomplete、spellcheck、dir 等)并拆掉包装结构,再调用 typeahead.destroy() 销毁内部实例(见 plugin.js 与 revert 实现)。

jQuery.fn.typeahead.noConflict() —— 返回 typeahead 插件引用并还原 jQuery.fn.typeahead:

var typeahead = jQuery.fn.typeahead.noConflict();
jQuery.fn._typeahead = typeahead;

另外,plugin.js 的 methods 中还实现了 enable/disable/isEnabled、activate/deactivate/isActive、open/close/isOpen、select、autocomplete、moveCursor(delta) 等控制方法,均通过 $.fn.typeahead(method, ...) 字符串调用方式分发。

4.2 Options 配置项

初始化 typeahead 时的顶层配置:

  • highlight —— 若为 true,渲染建议时,当前查询在文本节点中的匹配会被包进 strong 元素,class 为 {{classNames.highlight}}。默认 false。
  • hint —— 若为 false,不显示 hint。默认 true。
  • minLength —— 开始渲染建议前所需的最小字符数。默认 1。从 typeahead.js 源码看,minLength 会做数字类型校验,非数字时回退为 1。
  • classNames —— 覆盖默认 class 名,详见"Class Names"一节。

4.3 Datasets:数据集的配置

一个 typeahead 由一个或多个数据集组成。当用户修改输入值时,每个数据集都会尝试为新值渲染建议。大多数场景一个数据集即可;只有当你想按类别对建议分组时才需要多个数据集——例如 twitter.com 的搜索把结果分为最近搜索、趋势和账号,这正是多数据集适用的场景。

数据集的配置项:

  • source —— 建议的后备数据源。签名为 (query, syncResults, asyncResults) 的函数:syncResults 用于同步计算出的建议,asyncResults 用于异步计算出的建议(如来自 AJAX 请求)。source 也可以是 Bloodhound 实例(引擎内部通过 __ttAdapter 适配)。必填。
  • async —— 告知数据集是否期望异步建议。若未设置,会根据 source 的函数签名推断:若 source 期望 3 个参数,则 async 为 true。
  • name —— 数据集名称。会拼到 {{classNames.dataset}}- 之后作为容器元素的 class(即 tt-dataset-<name>)。只能包含下划线、短横线、字母(a-z)和数字。默认是随机数。
  • limit —— 显示的建议最大条数。默认 5。
  • display —— 决定一条建议的字符串表示,用于选中建议后回填输入框。可以是键字符串,也可以是 (suggestion) => string 的函数。默认将建议对象字符串化。
  • templates —— 渲染时使用的模板哈希。预编译模板是一个接收 JavaScript 对象作为第一个参数并返回 HTML 字符串的函数:
    • notFound —— 当给定查询 0 条建议时渲染。可为 HTML 字符串或预编译模板(上下文含 query)。
    • pending —— 当 0 条同步建议但预期有异步建议时渲染。可为 HTML 字符串或预编译模板(上下文含 query)。
    • header —— 数据集存在建议时渲染在顶部。可为 HTML 字符串或预编译模板(上下文含 query 和 suggestions)。
    • footer —— 数据集存在建议时渲染在底部。可为 HTML 字符串或预编译模板(上下文含 query 和 suggestions)。
    • suggestion —— 渲染单条建议。若设置,必须是预编译模板,关联的建议对象作为上下文。默认是 display 值包在 div 中,即 <div>{{value}}</div>。

4.4 自定义事件

typeahead 生命周期中会在 input 元素上触发以下自定义事件(可通过 bind/on 监听):

事件名触发时机回调参数
typeahead:activetypeahead 进入 active 状态—
typeahead:idletypeahead 进入 idle 状态—
typeahead:open结果容器打开—
typeahead:close结果容器关闭—
typeahead:change输入框失焦且值相比获得焦点时有变化(原生 change 事件的规范化版本)—
typeahead:render某数据集渲染了建议jQuery 事件对象、渲染的建议、是否异步获取的标记、所在数据集名称
typeahead:select选中了一条建议jQuery 事件对象、被选中的建议对象
typeahead:autocomplete发生自动补全jQuery 事件对象、用于补全的建议对象
typeahead:cursorchange结果容器光标移动jQuery 事件对象、光标移动到的建议对象
typeahead:asyncrequest发出异步建议请求jQuery 事件对象、当前查询、请求所属数据集名称
typeahead:asynccancel异步请求被取消jQuery 事件对象、当前查询、请求所属数据集名称
typeahead:asyncreceive异步请求完成jQuery 事件对象、当前查询、请求所属数据集名称

示例:

$('.typeahead').bind('typeahead:select', function(ev, suggestion) {
  console.log('Selection: ' + suggestion);
});

注意:每个事件携带的参数并不相同,请按上表核对各事件的具体参数列表。

从 typeahead.js 源码可以印证这些事件的触发链路:例如数据集渲染后触发 render 事件,异步请求的发起/取消/完成分别触发 asyncrequest/asynccancel/asyncreceive。键盘交互方面,_onEnterKeyed 在存在活动建议时执行 select 并阻止默认行为,_onTabKeyed 在有活动建议时选中、否则对顶部建议执行 autocomplete,方向键驱动 moveCursor(±1),Esc 执行 close。

4.5 Class Names 默认样式类

组件使用的默认 class 名如下:

用途默认 class
初始化为 typeahead 的 inputtt-input
hint 输入框tt-hint
菜单元素tt-menu
数据集元素tt-dataset
建议元素tt-suggestion
菜单为空时添加tt-empty
菜单打开时添加tt-open
光标移动到的建议tt-cursor
高亮文本的包裹元素tt-highlight

通过 classNames 选项可覆盖任意默认值:

$('.typeahead').typeahead({
  classNames: {
    input: 'Typeahead-input',
    hint: 'Typeahead-hint',
    selectable: 'Typeahead-selectable'
  }
});

五、内置分词器(Tokenizers)

Bloodhound 对数据与查询的 token 化依赖分词器,源码见 tokenizers.js,全部通过 Bloodhound.tokenizers 暴露:

  • Bloodhound.tokenizers.whitespace —— 按空白字符切分字符串(str.split(/\s+/));
  • Bloodhound.tokenizers.nonword —— 按非单词字符切分(str.split(/\W+/));
  • Bloodhound.tokenizers.obj.whitespace / obj.nonword —— 用于对象数据的版本,通过 Bloodhound.tokenizers.obj.whitespace('field1', 'field2') 指定参与 token 化的字段,返回一个可对 datum 进行多字段分词并合并结果的函数。

使用时注意两个 tokenizer 必须配对使用(数据分词与查询分词方式应一致),否则匹配会失效。

六、浏览器支持

README 声明的支持范围:

  • Chrome
  • Firefox 3.5+
  • Safari 4+
  • Internet Explorer 8+
  • Opera 11+

注意:typeahead.js 未在移动端浏览器上测试。从源码看,代码中有针对 IE 的兼容处理(如 typeahead.js 中 _hacks 对 IE 滚动条点击导致 blur 的补丁、plugin.js 中对 dir 属性 IE7 的容错),说明其对老版本 IE 做了专门适配。

七、版本策略

README 说明版本号遵循 <major>.<minor>.<patch> 格式,并遵循以下语义化版本规则:

  • 破坏向后兼容的变更 → 递增 major;
  • 不破坏向后兼容的新增功能 → 递增 minor;
  • Bug 修复与杂项变更 → 递增 patch。

当前仓库版本为 0.11.1(见 package.json)。CHANGELOG.md 与 doc/migration/0.10.0.md 提供了历史变更与迁移说明,升级前建议查阅。

八、测试与开发工作流

8.1 运行测试

测试使用 Jasmine 编写、由 Karma 驱动。安装依赖后,用 PhantomJS 运行完整测试套件:

$ npm test

package.json 中对应的脚本是 ./node_modules/karma/bin/karma start --single-run --browsers PhantomJS(见 package.json)。仓库在 karma.conf.js 中维护测试运行配置,测试用例分布在 test/bloodhound/ 与 test/typeahead/ 目录下(如 bloodhound_spec.js、plugin_spec.js 等),可用于验证各模块行为。

8.2 从源码构建

开发前需要安装开发依赖与 grunt-cli:

$ npm install
$ npm install -g grunt-cli

常用 Grunt 任务(见 Gruntfile.js):

  • grunt build —— 从源码构建 typeahead.js。构建流程依次执行:合并 src/common/utils.js 与 src/bloodhound/ 生成临时 bloodhound.js,合并 src/common/utils.js 与 src/typeahead/ 生成临时 typeahead.jquery.js,再用 grunt-umd 包装为 UMD 模块,最终产出 dist/bloodhound.js、dist/typeahead.jquery.js、dist/typeahead.bundle.js 及其 .min.js 压缩版,并写入版本号(见 Gruntfile.js)。
  • grunt lint —— 用 JSHint 检查源码与测试文件。
  • grunt watch —— 源文件修改后自动重新构建。
  • grunt server —— 在 localhost:8888 提供仓库根目录文件,方便用 test/playground.html 调试测试。
  • grunt dev —— 并行运行 grunt watch 与 grunt server。

九、贡献与支持

若计划为 typeahead.js 贡献代码,请先阅读 CONTRIBUTING.md。新贡献者可以从标记为 entry-level 的 issue 入手——这类问题通常改动较小,有助于熟悉代码库。

发现 Bug 可到仓库 Issues 页面提交。一般性问题可咨询社区,技术问题建议在 Stack Overflow 提问并打上 typeahead.js 标签。

十、许可证

typeahead.js 版权归 Twitter, Inc. 所有(Copyright 2013 Twitter, Inc.),以 MIT License 授权(见 LICENSE),可自由使用与二次分发。

总结

通过本文你可以看到,typeahead.js 的价值在于把"自动补全"这个看似简单的交互拆解为两个可独立复用的层次:Bloodhound 负责数据层的预取、缓存、限流与回填,Typeahead(jQuery 插件)负责 UI 层的渲染、键盘交互、提示与事件。无论是用 local + prefetch 做离线优先的第一级缓存,还是用 remote + wildcard + rateLimitBy 做后端建议服务的安全接入,都能在官方文档与本文的配置说明中找到可落地的方案。结合 doc/bloodhound.md、doc/jquery_typeahead.md 与 src/、test/ 目录下的源码,你可以进一步深入每一个配置项背后的实现细节。

【免费下载链接】typeahead.js typeahead.js is a fast and fully-featured autocomplete library 【免费下载链接】typeahead.js 项目地址: https://gitcode.com/gh_mirrors/ty/typeahead.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值