Vue 3组合式API中响应Props变更触发搜索的正确方案
Vue 3组合式API:监听搜索条件变更执行搜索
问题背景
- 我是一名正在学习Vue的React开发者,当前有一个基于Vue 3组合式API的项目,需求是让用户输入搜索条件,点击提交后展示搜索结果。
- 组件结构:
TheSidebar组件负责输入搜索条件并提交,它会向父组件SearchView触发事件,父组件再通过props将条件传递给TheResults组件,这部分逻辑运行正常。 - 核心需求:每当
searchCriteria更新时,TheResults组件能自动使用新条件执行搜索并展示结果。 - 在React中,我可以用
useEffect钩子监听props参数,当条件更新时执行搜索并更新本地状态,但在Vue中尝试用onUpdated钩子实现时出现了无限循环——因为更新cellSites状态会触发组件更新,进而再次触发onUpdated钩子,形成循环,而且onUpdated并非专门用于监听props变更的API。 - 我不想把搜索逻辑放到父组件
SearchView的事件处理器中(避免父组件职责越界),希望保持TheResults组件的独立性,自行处理搜索逻辑。
现有代码
SearchView.vue
<script setup lang="ts"> import { ref } from "vue"; import type { SearchCriteria } from '@/types/SearchCriteria'; import TheSidebar from '@/components/search/TheSidebar.vue' import TheResults from "@/components/search/TheResults.vue"; const searchCriteria = ref<SearchCriteria | undefined>(); const handleOnSearch = (criteria: SearchCriteria) => { searchCriteria.value = criteria; } </script> <template> <div class="q-main-with-sidebar"> <TheSidebar @search="handleOnSearch" /> <main> <TheResults :searchCriteria="searchCriteria" /> </main> </div> </template>
问题代码(TheResults.vue)
<script setup lang="ts"> import { onUpdated, ref } from "vue"; import { type SearchCriteria } from '@/types/SearchCriteria'; import { getCellLocations } from "@/actions/cellLocations"; import { type CellLocation } from "@/types/CellLocation"; const cellSites = ref<{data: CellLocation[], isLoading: boolean}>({data: [], isLoading: false}); interface IProps { searchCriteria: SearchCriteria | undefined; } const props = defineProps<IProps>(); onUpdated(async () => { if(props.searchCriteria) { cellSites.value = {data: [], isLoading: true}; let results = await getCellLocations(props.searchCriteria); cellSites.value = {data: results, isLoading: false}; } }); </script> <template> <loader v-if="cellSites.isLoading" size="small" type="pulsing" /> <span v-else>else</span> </template>
解决方案
在Vue 3组合式API中,对应React useEffect监听依赖的API是watch,我们可以用watch精准监听searchCriteria props的变化,只有当它变更时才执行搜索逻辑,避免无限循环。
修改后的TheResults.vue代码:
<script setup lang="ts"> import { ref, watch } from "vue"; import { type SearchCriteria } from '@/types/SearchCriteria'; import { getCellLocations } from "@/actions/cellLocations"; import { type CellLocation } from "@/types/CellLocation"; const cellSites = ref<{data: CellLocation[], isLoading: boolean}>({data: [], isLoading: false}); interface IProps { searchCriteria: SearchCriteria | undefined; } const props = defineProps<IProps>(); // 监听searchCriteria的变化 watch(() => props.searchCriteria, async (newCriteria) => { if(newCriteria) { // 开始加载 cellSites.value = {data: [], isLoading: true}; // 执行搜索 const results = await getCellLocations(newCriteria); // 更新结果 cellSites.value = {data: results, isLoading: false}; } }, { immediate: true }); // immediate: true 表示组件挂载时就执行一次(如果初始有searchCriteria的话) </script> <template> <loader v-if="cellSites.isLoading" size="small" type="pulsing" /> <span v-else>else</span> </template>
说明
watch的作用:watch会精准监听指定的响应式数据(这里是props.searchCriteria),只有当该数据发生变化时,才会执行回调函数,不会像onUpdated那样在组件任何更新时都触发。- 避免无限循环:因为回调函数只在
searchCriteria变更时执行,更新cellSites不会触发watch的回调(除非cellSites被监听,但这里没有),所以不会形成循环。 immediate选项:如果需要组件挂载时就根据初始的searchCriteria执行一次搜索,可以保留{ immediate: true }配置;如果只需要在用户提交后(即searchCriteria从无到有/变更时)执行,可以去掉这个选项。
内容的提问来源于stack exchange,提问作者Leigh.D
相关产品推荐
相关产品推荐

