- 前端
- UI组件
- 设计系统
【免费下载链接】vue-devui
基于全新 DevUI Design 设计体系的 Vue3 组件库,面向研发工具的开源前端解决方案。
在研发工具类产品中,输入框往往不只是"打字的地方":用户需要根据已输入的部分字符快速推断出完整的候选项(如语言名、命令、标签、仓库名)。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):
handleSearch(term, enableLazyLoad)先把term转为小写;- 若开启了懒加载(
enableLazyLoad=true),直接取source全量数据展示(懒加载场景下不做前端过滤); - 否则若未提供
searchFn,走内置defaultSearchFn:对每个元素先经formatter取展示文本,再toLowerCase().startsWith(term)做前缀匹配; - 若提供了
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 参数
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| size | AutoCompleteSize | 'md' | 输入框尺寸,三种选择:'lg'、'md'、'sm' |
| placeholder | string | 'Search' | 占位符 |
| source | SourceType | — | 必选;有search-fn的情况下可以不必选 |
| allow-empty-value-search | boolean | false | 输入框 value 为空时,是否进行搜索提示操作 |
| append-to-body | boolean | false | 下拉弹出是否 append to body |
| position | Placement[] | ['bottom'](文档口径;源码默认['bottom','top','left','right']自动择位) | 指定下拉框与输入框的相对位置 |
| disabled | boolean | false | 是否禁用 |
| delay | number | 300 | 只有 delay 时间经过后并且未输入新值,才做搜索查询(ms) |
| disabled-key | string | — | 禁用单个选项时,对象中禁用标志的字段名 |
| formatter | (item: any) => string | defaultFormatter | 格式化函数,决定每项展示文本 |
| is-searching | boolean | false | 是否在搜索中,用于控制 searching 插槽是否显示 |
| scene-type | string | — | 值为'select'、'suggest' |
| search-fn | (term: string) => Array<any> | defaultSearchFn | 自定义搜索过滤,可为异步函数 |
| tips-text | string | '最近输入' | 最近输入提示文字 |
| latest-source | Array<any> | — | 最近输入数据 |
| value-parser | (item: any) => any | defaultValueParse | 对选中后数据进行处理 |
| enable-lazy-load | boolean | false | 是否允许懒加载 |
| width | number | 400 | 调整宽度(px) |
| show-animation | boolean | true | 是否开启动画(弹层 fade 过渡) |
| prefix | string | — | 自定义前缀图标 |
| suffix | string | — | 自定义后缀图标 |
| clearable | boolean | false | 是否可清空(见 d-input 一键清空) |
| show-glow-style | boolean | true | 是否显示悬浮发光效果 |
另外,从 auto-complete-types.ts 的 props 定义中还可以看到一个未在文档表格中列出的maxHeight(默认300,单位 px),它限制下拉列表ul的最大高度,也是懒加载滚动判定的参考值。
AutoComplete 事件
| 事件 | 类型 | 说明 |
|---|---|---|
| load-more | function | 懒加载触发事件,配合enable-lazy-load使用;使用$event.loadFinish()关闭 loading 状态,其中$event为 AutoCompleteComponent 的实例 |
| select-value | function(value) | 选择选项之后的回调函数(经formatter处理后的文本) |
| trans-input-focus-emit | function | Input 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 组件库,面向研发工具的开源前端解决方案。
相关推荐
Arco Design AutoComplete 自动补全组件深度指南:API 解析、数据源与自定义输入实战
Arco Design AutoComplete 自动补全组件深度指南:API 解析、数据源与自定义输入实战 自动补全(AutoComplete)是 Arco
UI组件前端设计系统rsuite AutoComplete 实战:用动态数据源实现邮箱域名自动补全
rsuite AutoComplete 实战:用动态数据源实现邮箱域名自动补全 导读 本文以 rsuite 官方文档中的邮箱自动补全示例( docs/pages
前端UI组件Ant Design Blazor AutoComplete 组件实战指南:自动补全输入框的配置、查询模式与源码原理
Ant Design Blazor AutoComplete 组件实战指南:自动补全输入框的配置、查询模式与源码原理 AutoComplete(自动完成)是 A
前端UI组件设计系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考