核心是用 ref 管理搜索参数并配合 watch 实现双向联动:keyword、categoryId、priceRange 等均定义为 ref,v-model 直接绑定;watch 参数变化驱动 UI 同步,防抖处理搜索与 URL 更新,并归一化空值。

核心思路是用 ref 或 reactive 管理搜索参数,配合 watch 监听参数变化并反向更新表单控件值,同时确保用户手动输入时能及时同步回参数——形成双向联动。
用 ref 统一管理搜索参数
把所有搜索字段(如关键词、分类 ID、价格范围等)定义为独立的 ref,方便细粒度控制和 watch:
- const keyword = ref('')
- const categoryId = ref(null)
- const priceRange = ref([0, 1000])
这样既保持响应性,又避免在模板中过度嵌套 reactive 对象,便于与表单 v-model 直接绑定。
表单控件双向绑定 + 手动触发同步
模板中直接用 v-model 绑定 ref 变量,用户输入自动更新参数;但要注意:某些场景(如 URL 参数初始化、筛选项点击)会直接修改参数,需让表单“感知”并重置显示值:
- input 输入框:
v-model="keyword" - select 下拉框:
v-model="categoryId" - el-slider 等非原生控件:用
:model-value+@update:model-value显式同步
watch 参数变化,驱动 UI 同步(关键)
当外部逻辑(如路由解析、历史记录还原、API 返回默认值)修改了参数 ref,需让表单控件刷新显示。用 watch 监听参数,赋值给对应控件的绑定变量即可:
- watch(() => route.query.q, (q) => { keyword.value = q || '' })
- watch([categoryId, priceRange], () => { performSearch() }, { deep: true })
注意:避免在 watch 回调里直接改 v-model 绑定的 ref(会造成循环),而是确保监听的是「参数源」,更新的是「UI 展示态」——只要参数和表单都指向同一 ref,就天然一致。
防抖搜索 + 参数归一化处理
用户频繁输入时,不希望每次 keystroke 都触发搜索或 URL 更新。可封装一个带防抖的同步函数:
- 用
lodash.debounce或useDebounceFn(VueUse)包装 search 函数 - 在 watch 中只更新参数,搜索逻辑单独触发;URL 同步也延后执行,避免 history push 太密集
- 对空字符串、null、undefined 做归一化(如 '' → undefined),保持 query 参数干净

















