typeahead.js 完整指南:基于 jQuery 的高性能自动补全库与 Bloodhound 建议引擎
【免费下载链接】typeahead.jstypeahead.js is a fast and fully-featured autocomplete library项目地址: 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 交互。
两者可以单独使用,但组合使用能提供最丰富的体验。从源码结构看,这一分层非常清晰:
- 引擎侧:
src/bloodhound/目录下包含 bloodhound.js(引擎主体)、search_index.js(内部搜索索引)、prefetch.js、remote.js、transport.js(网络传输)、persistent_storage.js(本地存储)、lru_cache.js(LRU 缓存)、tokenizers.js(分词器)与 options_parser.js(选项解析)。 - UI 侧:
src/typeahead/目录下包含 plugin.js(jQuery 插件入口)、typeahead.js(核心控制器)、input.js(输入框)、menu.js(菜单)、dataset.js(数据集)、event_bus.js(事件总线)、highlight.js(高亮)等。
在 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:active | typeahead 进入 active 状态 | — |
typeahead:idle | typeahead 进入 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 的 input | tt-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 testpackage.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.jstypeahead.js is a fast and fully-featured autocomplete library项目地址: https://gitcode.com/gh_mirrors/ty/typeahead.js
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考