☰
vue-devui AutoComplete 自动补全组件实战:数据源配置、自定义匹配、懒加载与源码实现解析
2026/10/14 16:21:58 网站建设 项目流程
  • 前端
  • UI组件
  • 设计系统

【免费下载链接】vue-devui

基于全新 DevUI Design 设计体系的 Vue3 组件库,面向研发工具的开源前端解决方案。

项目地址:https://gitcode.com/DevCloudFE/vue-devui
点击查看免费下载

在研发工具类产品中,输入框往往不只是"打字的地方":用户需要根据已输入的部分字符快速推断出完整的候选项(如语言名、命令、标签、仓库名)。vue-devui 的d-auto-complete(AutoComplete 自动补全)组件正是为此设计:它内置防抖搜索、键盘导航、下拉弹层定位、最近输入展示与滚动懒加载等能力。本文基于官方组件文档 auto-complete/index.md 展开,并结合 组件源码 与 测试用例,带你完整掌握 AutoComplete 的每一项配置、事件与插槽,并理解其底层实现机制。

何时使用

当需要根据用户输入的部分字符推断出他可能想要输入的内容时,使用 AutoComplete。典型场景包括:

  • 搜索框联想(输入前缀自动补全);
  • 研发工具中的命令/语言/标签名快速选择;
  • 配合"最近输入"能力,降低重复操作成本。

组件的导出入口在 packages/devui-vue/devui/auto-complete/index.ts,它被归类为"数据录入"类组件,通过install方法以DAutoComplete名称注册:

// packages/devui-vue/devui/auto-complete/index.ts export default { title: 'AutoComplete 自动补全', category: '数据录入', status: '100%', install(app: App): void { app.component(AutoComplete.name, AutoComplete); }, };

基本用法

通过source设置自动完成的数据源。数据源支持string[]或{ label, disabled }对象数组两种形式(类型定义见 auto-complete-types.ts)。

<template> <d-auto-complete :delay="1000" :source="source" v-model="value" :allow-empty-value-search="allowEmptyValueSearch" :select-value="selectValue" :trans-input-focus-emit="transInputFocusEmit" :position="position" :width="420" :append-to-body="false" > </d-auto-complete> </template> <script> import { defineComponent, ref } from 'vue'; export default defineComponent({ setup() { const value = ref(''); const allowEmptyValueSearch = ref(true); const source = ref([ 'C#', 'C', 'C++', 'CPython', 'Java', 'JavaScript', 'Go', 'Python', 'Ruby', 'F#', 'TypeScript', 'SQL', 'LiveScript', 'CoffeeScript', ]); const selectValue = (e) => { console.log('selectValue', e); }; const transInputFocusEmit = () => { console.log('transInputFocusEmit'); }; const position = ref(['bottom']); return { value, source, allowEmptyValueSearch, transInputFocusEmit, selectValue, position, }; }, }); </script>

几个关键行为的源码依据:

  • 防抖搜索:输入事件并非立即触发搜索。在 use-input-handle.ts 中,onInput先发出update:modelValue更新绑定值,随后通过debounce(onInputCb, delay.value)以delay(默认 300ms)为等待时间防抖执行handleSearch。每次输入都会清空上一次的定时器,即"只有 delay 时间经过后且未输入新值,才做搜索查询"。防抖执行期间searchStatus被置为true,用于配合searching插槽展示搜索中状态。
  • 空值搜索:allowEmptyValueSearch为false时,输入内容为空直接清空searchList且不显示"无匹配"提示;为true时,聚焦空输入框也会执行全量匹配(见 use-searchfn.ts 中handleSearch对空 term 的分支处理)。
  • 弹层定位:下拉弹层由FlexibleOverlay渲染,position接收一个位置候选数组,弹层按顺序尝试落位;append-to-body为true时通过<Teleport to="body">将弹层传送到 body,避免被父容器overflow: hidden裁剪(见 auto-complete.tsx 的renderDropdown)。需要说明的是,文档中position默认值标注为['bottom'],而从源码 props 定义看,未传position时实际默认为['bottom', 'top', 'left', 'right'],由 overlay 自动择位。
  • 失焦/点击外部关闭:组件根节点绑定了click-outside指令,触发handleClose将弹层隐藏并重置搜索状态。

尺寸

支持sm、md、lg三种尺寸,默认为md。尺寸最终体现为根节点的d-auto-complete--sm / d-auto-complete--lg修饰类(md不加修饰类)。

<template> <h4>Small</h4> <d-auto-complete size="sm" :source="source" v-model="value1" :width="420"> </d-auto-complete> <h4>Middle</h4> <d-auto-complete :source="source" v-model="value2" :width="420"> </d-auto-complete> <h4>Large</h4> <d-auto-complete size="lg" :source="source" v-model="value3" :width="420"> </d-auto-complete> </template> <script> import { defineComponent, ref } from 'vue'; export default defineComponent({ setup() { const value1 = ref(''); const value2 = ref(''); const value3 = ref(''); const source = ref(['C#', 'C', 'C++']); return { value1, value2, value3, source }; }, }); </script>

值得一提的是,AutoComplete 会响应d-form上下文:从源码看,autoCompleteSize计算属性优先取formContext?.size,未传size时会继承表单级别的尺寸;isDisabled同样优先取formContext?.disabled(见 auto-complete.tsx 中inject(FORM_TOKEN)相关逻辑)。

设置禁用

通过disabled设置是否禁用。禁用后输入框disabled属性生效,且点击输入框不会展开下拉菜单(toggleMenu中会先判断isDisabled)。

<template> <d-row type="flex"> <d-col :flex="4"> <d-auto-complete :source="source" v-model="value" :disabled="isDisabled"> </d-auto-complete> </d-col> <d-col :flex="2"> <d-button id="primaryBtn" @click="toggle" style="margin-left:10px"> {{ isDisabled ? '点击启用' : '点击禁用' }} </d-button> </d-col> </d-row> </template> <script> import { defineComponent, ref } from 'vue'; export default defineComponent({ setup() { const value = ref(''); const source = ref([ 'C#', 'C', 'C++', 'CPython', 'Java', 'JavaScript', 'Go', 'Python', 'Ruby', 'F#', 'TypeScript', 'SQL', 'LiveScript', 'CoffeeScript', ]); const isDisabled = ref(false); function toggle() { isDisabled.value = !isDisabled.value; } return { value, source, isDisabled, toggle }; }, }); </script>

对应地,clearable的清空图标只在clearable && !isDisabled时渲染(见源码showClearable计算属性),测试用例 auto-complete.spec.ts 中的 "disabled" 用例验证了切换disabled后input.disabled为true。

自定义数据匹配方法

当数据源是对象数组、或匹配规则不是简单的前缀匹配时,可以通过search-fn自定义数据的匹配方法和返回的数据格式;配合formatter决定每一项的展示文本,配合disabled-key指定对象中"禁用"字段名。

<template> <d-auto-complete v-model="value" :search-fn="searchFn" disabled-key="disabled" is-searching :delay="1000" :formatter="formatter" > <template #searching="slotProps"> <div> {{ `searching: ${slotProps}` }} </div> </template> </d-auto-complete> </template> <script> import { defineComponent, ref } from 'vue'; export default defineComponent({ setup() { const value = ref(''); const mySource = ref([ { label: 'C#', disabled: false }, { label: 'C++', disabled: false }, { label: 'CPython', disabled: false }, { label: 'Java', disabled: false }, { label: 'JavaScript', disabled: false }, { label: 'Go', disabled: false }, { label: 'Ruby', disabled: false }, { label: 'F#', disabled: false }, { label: 'TypeScript', disabled: false }, { label: 'SQL', disabled: true }, { label: 'LiveScript', disabled: false }, { label: 'CoffeeScript', disabled: false }, ]); const formatter = (item) => { return item.label; }; // trem:input输入内容 const searchFn = async (trem) => { let arr = []; await new Promise((resolve) => { setTimeout(() => { resolve(); }, 1000); }); mySource.value.forEach((item) => { let cur = item.label; cur = cur.toLowerCase(); if (cur.startsWith(trem)) { arr.push(item); } }); return arr; }; return { value, searchFn, formatter }; }, }); </script>

源码层面的匹配流程(use-searchfn.ts):

  1. handleSearch(term, enableLazyLoad)先把term转为小写;
  2. 若开启了懒加载(enableLazyLoad=true),直接取source全量数据展示(懒加载场景下不做前端过滤);
  3. 否则若未提供searchFn,走内置defaultSearchFn:对每个元素先经formatter取展示文本,再toLowerCase().startsWith(term)做前缀匹配;
  4. 若提供了searchFn(可以是异步函数),await其返回结果。

也就是说,默认的匹配策略是"展示文本小写后以输入串开头",而非子串包含;如果你需要模糊/包含匹配,或对接后端接口,就必须自己实现search-fn。

is-searching为true且提供了searching插槽时,防抖等待期间(searchStatus为true)下拉区会渲染该插槽,参数为当前输入内容——上面 demo 中等待 1 秒的搜索过程就会展示searching: xxx。测试用例 "Customized data matching method" 验证了:输入c后searching模板出现且内容为c;输入s后SQL项因disabled: true与disabled-key="disabled"的组合而呈现.disabled样式且不可选。

自定义模板展示

通过item、nothing插槽自定义下拉选项与"无匹配项"提示。

<template> <d-auto-complete :source="source" v-model="value"> <template #item="slotProps"> <div>第{{ slotProps.index }}项: {{ slotProps.item }}</div> </template> <template #nothing="slotProps"> <div> {{ `没有匹配项: ${slotProps}` }} </div> </template> </d-auto-complete> </template> <script> import { defineComponent, ref } from 'vue'; export default defineComponent({ setup() { const value = ref(''); const source = ref([ 'C#', 'C', 'C++', 'CPython', 'Java', 'JavaScript', 'Go', 'Python', 'Ruby', 'F#', 'TypeScript', 'SQL', 'LiveScript', 'CoffeeScript', ]); return { value, source }; }, }); </script>

从 use-custom-template.ts 可以看到插槽参数的传递细节:

  • #item收到的是{ item, index }对象,item为当前数据项(string或SourceItemObj),index为下标;
  • #nothing与#searching收到的都是当前输入值(modelValue);
  • 这三个插槽在根组件中被包装成内部模板后,通过provide/inject(DropdownPropsKey)传递给下拉子组件 dropdown.tsx 渲染。

最近输入

通过latest-source设置最近输入。当输入框聚焦且当前值为空时,下拉区顶部会显示tips-text(默认文案"最近输入")提示,并将列表内容替换为latest-source:

<template> <d-auto-complete :source="source" v-model="value" :latest-source="latestSource"> </d-auto-complete> </template> <script> import { defineComponent, ref } from 'vue'; export default defineComponent({ setup() { const value = ref(''); const latestSource = ref(['JavaScript', 'TypeScript']); const source = ref([ 'C#', 'C', 'C++', 'CPython', 'Java', 'JavaScript', 'Go', 'Python', 'Ruby', 'F#" , 'TypeScript', 'SQL', 'LiveScript', 'CoffeeScript', ]); return { value, source, latestSource }; }, }); </script>

实现上,onFocus中会依次调用handleSearch(modelValue)与recentlyFocus(latestSource.value),后者直接把searchList覆写为最近输入列表(use-searchfn.ts 中的recentlyFocus);dropdown.tsx 中则通过latestSource && !modelValue条件渲染提示文案行,文案经 i18n 的latestInput键处理,实际提示文字由tips-textprop 控制。一旦开始输入,就会回到正常的搜索匹配流程。

启用懒加载

enable-lazy-load开启懒加载,适合候选数据量很大、需要滚动分页加载的场景。使用要点:

  • 必须配合load-more回调:下拉列表滚动到底部时触发;
  • 新数据追加到source后,调用组件实例方法loadFinish()关闭 loading 状态并刷新列表;
  • value-parser可对选中后的数据做二次加工(如对象转字符串);
  • scene-type可选'select'/'suggest'。
<template> <d-auto-complete ref="autoCompleteRef" :source="source" v-model="value" enable-lazy-load :load-more="loadMore" scene-type="select" :value-parser="valueParser" > </d-auto-complete> </template> <script> import { defineComponent, ref } from 'vue'; export default defineComponent({ setup() { const value = ref(''); const source = ref([ 'C#', 'C', 'C++', 'CPython', 'Java', 'JavaScript', 'Go', 'Python', 'Ruby', 'F#', 'TypeScript', 'SQL', 'LiveScript', 'CoffeeScript', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', ]); const autoCompleteRef = ref(null); const loadMore = () => { setTimeout(() => { source.value.push('lazyData' + source.value.length); autoCompleteRef.value?.loadFinish(); }, 3000); }; const valueParser = (e) => { return e + '123'; }; return { value, source, loadMore, valueParser, autoCompleteRef }; }, }); </script>

源码中 use-lazy-handle.ts 的滚动判定逻辑为:clientHeight + scrollTop >= scrollHeight && clientHeight + scrollTop >= maxHeight时调用props.loadMore()并置showLoading(下拉区套v-loading指令显示加载动画);loadFinish方法通过ctx.expose暴露到组件实例上,内部会执行一次handleSearch(modelValue, enableLazyLoad)再关闭 loading。懒加载模式下搜索不再做前端过滤(handleSearch中enableLazyLoad=true分支直接取全量source),过滤交由你的分页/后端逻辑完成。

AutoComplete 参数

参数名类型默认值说明
sizeAutoCompleteSize'md'输入框尺寸,三种选择:'lg'、'md'、'sm'
placeholderstring'Search'占位符
sourceSourceType—必选;有search-fn的情况下可以不必选
allow-empty-value-searchbooleanfalse输入框 value 为空时,是否进行搜索提示操作
append-to-bodybooleanfalse下拉弹出是否 append to body
positionPlacement[]['bottom'](文档口径;源码默认['bottom','top','left','right']自动择位)指定下拉框与输入框的相对位置
disabledbooleanfalse是否禁用
delaynumber300只有 delay 时间经过后并且未输入新值,才做搜索查询(ms)
disabled-keystring—禁用单个选项时,对象中禁用标志的字段名
formatter(item: any) => stringdefaultFormatter格式化函数,决定每项展示文本
is-searchingbooleanfalse是否在搜索中,用于控制 searching 插槽是否显示
scene-typestring—值为'select'、'suggest'
search-fn(term: string) => Array<any>defaultSearchFn自定义搜索过滤,可为异步函数
tips-textstring'最近输入'最近输入提示文字
latest-sourceArray<any>—最近输入数据
value-parser(item: any) => anydefaultValueParse对选中后数据进行处理
enable-lazy-loadbooleanfalse是否允许懒加载
widthnumber400调整宽度(px)
show-animationbooleantrue是否开启动画(弹层 fade 过渡)
prefixstring—自定义前缀图标
suffixstring—自定义后缀图标
clearablebooleanfalse是否可清空(见 d-input 一键清空)
show-glow-stylebooleantrue是否显示悬浮发光效果

另外,从 auto-complete-types.ts 的 props 定义中还可以看到一个未在文档表格中列出的maxHeight(默认300,单位 px),它限制下拉列表ul的最大高度,也是懒加载滚动判定的参考值。

AutoComplete 事件

事件类型说明
load-morefunction懒加载触发事件,配合enable-lazy-load使用;使用$event.loadFinish()关闭 loading 状态,其中$event为 AutoCompleteComponent 的实例
select-valuefunction(value)选择选项之后的回调函数(经formatter处理后的文本)
trans-input-focus-emitfunctionInput focus 时回调函数

此外,组件通过emits声明了update:modelValue、clear、blur三个标准事件(见 auto-complete.tsx),可配合v-model、@clear、@blur使用;作为输入框,input、keyup等原生事件也可正常触发(测试用例 "bindEvents work" 有验证)。

选中回调select-value的触发时机在 use-select-handle.ts:点击某一项后依次执行emit('update:modelValue', formatter(item))→handleClose()关闭弹层 →handleSearch刷新列表 → 回调selectValue。

AutoComplete 插槽

插槽名说明
item自定义选项展示模板。slotProps:{ index: 下标索引, item: 当前项内容 }
nothing没有匹配项时的展示结果。slotProps:输入内容
searching自定义"搜索中"显示模板。slotProps:输入内容
prefix输入框头部内容(图标等)
suffix输入框尾部内容
prepend输入框前置内容
append输入框后置内容

prefix/suffix除 prop 图标名(经Icon组件渲染)外也支持具名插槽;prepend/append插槽分别渲染在输入框外层的前、后位置。测试用例 "prefix/suffix/prepend/append slot work" 验证了四种插槽均可正常渲染。

类型定义

以下类型与默认函数在 auto-complete-types.ts 中对外导出(export * from './src/auto-complete-types'),可在业务代码中直接import使用。

SourceType
interface SourceItemObj { label: string; disabled: boolean; [propName: string]: unknown; } type SourceType = Array<string> | Array<SourceItemObj>;
Placement
type Placement = | 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end';
defaultSearchFn
defaultSearchFn = (term: string) => { type ItemType = typeof source.value[0]; const arr: ItemType[] = []; source.value.forEach((item) => { let cur = formatter.value(item as ItemType); cur = cur.toLowerCase(); if (cur.startsWith(term)) { arr.push(item); } }); return arr as SourceType; };

term为输入的关键字。默认匹配是"展示文本小写后的前缀匹配",与自定义search-fn的行为对比可见:若你的search-fn用includes实现,匹配效果会明显宽于默认行为。

defaultFormatter
defaultFormatter = (item: string | SourceItemObj) => { if (typeof item === 'string') { return item; } return item !== null ? item.label || item.toString() : ''; };

item为数据项。字符串原样返回;对象取label字段。

defaultValueParse
defaultValueParse = (item) => item;

默认原样返回选中项,不做加工。

AutoCompleteSize
type AutoCompleteSize = 'sm' | 'md' | 'lg';

键盘交互与选中高亮

AutoComplete 内置完整的键盘操作,实现在 use-keyboard-select.ts:

  • ArrowDown / ArrowUp:在候选项间循环移动高亮(hoverIndex),到末项再按 Down 回到首项,反之亦然;每次移动都会scrollIntoView保证高亮项可见;
  • Enter:选中当前高亮项,走与点击相同的selectOptionClick流程;
  • Escape:弹层展开且有候选/搜索中/无匹配模板时,关闭弹层。

高亮项(hoverIndex)与"已选中项"(selectedIndex,即selected类)是两个独立状态:下拉渲染时[ns.e('dropdown-bg')]: hoverIndex === index控制悬停背景,selected类标记当前值对应的项。auto-complete.spec.ts 的 "init render & KeyboardEvent" 用例完整走了一遍该链路:输入c后连续 ArrowDown/ArrowUp 移动高亮,最终 Enter 后value变为C++,Escape 后弹层关闭。

与 Form 的联动

从 use-auto-complete-render.ts 的源码结构看,AutoComplete 深度接入了d-form/d-form-item体系:

  • inject(FORM_TOKEN):继承表单级disabled与size;
  • inject(FORM_ITEM_TOKEN):当validateState === 'error'时输入框 wrapper 追加错误样式类;modelValue变化时自动触发formItemContext.validate('change'),即 AutoComplete 作为受控控件参与表单校验;
  • 校验反馈(showFeedback)时追加has-feedback相关类名。

因此在d-form-item中放置d-auto-complete,可以直接获得校验态样式与值变更自动校验能力,无需额外配置。

小结

vue-devui 的d-auto-complete以"输入 → 防抖 → 匹配 → 弹层展示 → 键盘/鼠标选择 → 回写 modelValue"为核心链路,覆盖了自动补全的常见需求:

  • 静态数据前缀联想:source+ 默认匹配即可;
  • 远程/对象数据:search-fn(可异步)+formatter+disabled-key;
  • 视觉定制:item/nothing/searching插槽与prefix/suffix/prepend/append插槽;
  • 大列表:enable-lazy-load+load-more+ 实例方法loadFinish();
  • 体验细节:delay防抖、position/append-to-body弹层控制、latest-source最近输入、clearable一键清空、show-glow-style发光效果。

所有行为均可在当前仓库中对照验证:主组件 auto-complete.tsx、各组合式函数(composables/目录)、下拉渲染 dropdown.tsx 以及测试 auto-complete.spec.ts,配合官方文档 auto-complete/index.md 中的示例,可快速定位每一个配置项的具体作用与默认值。

  • 前端
  • UI组件
  • 设计系统

【免费下载链接】vue-devui

基于全新 DevUI Design 设计体系的 Vue3 组件库,面向研发工具的开源前端解决方案。

项目地址:https://gitcode.com/DevCloudFE/vue-devui
点击查看免费下载

相关推荐

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询