search-problems-analysis.md
7.44 KB
搜索页面问题分析报告
📋 问题概述
分析 src/pages/search/index.vue 的搜索功能,发现以下关键问题。
🔴 问题 1: 清空关键词后的状态混乱
问题描述
用户搜索后,如果清空搜索关键词,会看到错误的界面状态。
复现步骤
- 输入关键词"保险",点击搜索
- 看到搜索结果(假设有 50 条)
- 删除输入框中的关键词(清空)
- 错误:界面显示"初始状态"(搜索图标 + "输入关键词开始搜索")
- 期望:界面应该显示"暂无搜索结果"(空状态图 + "暂无搜索结果")
根本原因
代码第 344-353 行的 watch(searchKeyword, ...) 逻辑有问题:
watch(searchKeyword, (newVal) => {
if (newVal.trim()) {
hasSearched.value = true // ✅ 有关键词时,设置 hasSearched = true
} else {
hasSearched.value = false // ❌ 没有关键词时,重置 hasSearched = false
}
})
问题:
- 当用户清空关键词时,
hasSearched被重置为false - 导致模板第 121-125 行的"初始状态"被渲染:
vue <view v-else class="flex flex-col items-center justify-center py-[120rpx]"> <IconFont name="search" class="text-gray-300 mb-[24rpx]" size="64" /> <view class="text-[#6B7280] text-[28rpx]">搜索培训资料、案例、产品</view> <view class="text-[#9CA3AF] text-[24rpx]">mt-[12rpx]">输入关键词开始搜索</view> </view>
用户影响
- 用户体验差:清空关键词后,不知道自己之前搜索过
- 逻辑不连贯:已经搜索过,应该保持"已搜索状态"
修复方案
需要区分两种情况:
- 从未搜索过:显示"初始状态"(引导用户搜索)
- 搜索过但清空了关键词:显示"暂无搜索结果"(提示用户没有结果)
// 新增状态:记录是否曾经搜索过
const hasSearchedOnce = ref(false)
watch(searchKeyword, (newVal) => {
if (newVal.trim()) {
hasSearched.value = true
hasSearchedOnce.value = true // ✅ 标记已经搜索过
} else {
// ❌ 不要重置 hasSearched
// hasSearched.value = false // 删除这行
// ✅ 保持 hasSearched 不变,让 computed 返回空结果
}
})
🟡 问题 2: 模板条件判断逻辑不清晰
问题描述
模板第 49-125 行的条件判断逻辑复杂且容易出错。
当前逻辑
<!-- 有结果 -->
<view v-if="searchResults.length > 0">
<!-- 显示结果 -->
</view>
<!-- 空结果(但 hasSearched = true) -->
<view v-else-if="hasSearched">
<!-- 显示空状态 -->
</view>
<!-- 初始状态(hasSearched = false) -->
<view v-else>
<!-- 显示初始状态 -->
</view>
问题
-
hasSearched的语义不清晰:表示"是否搜索过"还是"应该显示结果"? -
hasSearched被多处修改,难以维护
修复方案
使用更清晰的变量名和逻辑:
// 方案 1:使用两个状态变量
const isInitialState = ref(true) // 是否初始状态
const searchResults = ref([])
// 方案 2:使用枚举状态
const searchState = ref('idle') // 'idle' | 'searching' | 'results' | 'empty'
🟡 问题 3: 切换分类时的状态不一致
问题描述
用户搜索后切换分类,可能看到错误的结果数量。
复现步骤
- 在"全部"分类搜索"保险"(假设有 50 条结果)
- 切换到"产品"分类(假设有 30 条结果)
- 切换到"资料"分类(假设有 20 条结果)
预期行为
- 每次切换分类,结果数量应该正确更新
实际行为
- 需要验证:
listRenderKey是否正确触发了重新渲染 - 需要验证:
searchResultscomputed 是否正确响应activeTabId的变化
潜在问题
第 51 行的 :key="listRenderKey" 用于强制重新渲染,但这是不优雅的做法:
<view
v-if="searchResults.length > 0"
:key="listRenderKey" // ⚠️ 通过改变 key 强制重新渲染
>
更好的方案:
- 依赖 Vue 的响应式系统,自动重新计算
searchResults - 不需要手动维护
listRenderKey
🟢 问题 4: 实时搜索 vs 手动搜索冲突
问题描述
watch(searchKeyword) 实现了实时搜索,但 handleSearch() 也实现了手动搜索,两者可能产生冲突。
当前实现
// 实时搜索(第 344 行)
watch(searchKeyword, (newVal) => {
if (newVal.trim()) {
hasSearched.value = true
}
})
// 手动搜索(第 292 行)
const handleSearch = () => {
if (searchKeyword.value.trim()) {
hasSearched.value = true
}
}
冲突点
- 用户输入"保" → 实时搜索触发 →
hasSearched = true - 用户继续输入"保险" → 实时搜索再次触发 →
hasSearched = true - 用户点击搜索按钮 → 手动搜索触发 →
hasSearched = true(冗余)
建议
-
只保留实时搜索:移除
handleSearch中的hasSearched设置 -
或者只保留手动搜索:移除
watch(searchKeyword)中的逻辑 -
或者明确区分:
- 输入时:只更新关键词,不设置
hasSearched - 点击搜索按钮时:才设置
hasSearched = true
- 输入时:只更新关键词,不设置
📊 测试建议
手动测试清单
- 输入关键词,验证搜索结果正确
- 清空关键词,验证显示"暂无搜索结果"(而非"初始状态")
- 搜索后切换分类,验证结果数量正确更新
- 清空关键词后切换分类,验证显示正确
- 快速输入和删除关键词,验证不出现闪烁或错误
自动化测试
运行测试文件:
pnpm test src/pages/search/index.test.js
🔧 推荐修复方案
方案 1: 最小改动(快速修复)
只修复问题 1,改动最小:
// 第 344-354 行
watch(searchKeyword, (newVal) => {
if (newVal.trim()) {
hasSearched.value = true
}
// ❌ 删除 else 分支
// else {
// hasSearched.value = false
// }
})
影响:
- ✅ 清空关键词后,
hasSearched保持true - ✅ 显示"暂无搜索结果"(而非"初始状态")
- ⚠️ 但用户刷新页面后,仍然显示初始状态(符合预期)
方案 2: 重构状态管理(推荐)
使用更清晰的状态变量:
// 搜索状态枚举
const SearchState = {
IDLE: 'idle', // 初始状态(从未搜索)
SEARCHING: 'searching', // 搜索中
RESULTS: 'results', // 有结果
EMPTY: 'empty' // 无结果
}
const searchState = ref(SearchState.IDLE)
const searchKeyword = ref('')
// 计算属性:根据搜索状态和结果数量返回应该显示的状态
const displayState = computed(() => {
if (!searchKeyword.value.trim() && searchState.value === SearchState.IDLE) {
return 'initial'
}
if (searchResults.value.length > 0) {
return 'results'
}
return 'empty'
})
📝 总结
主要问题
- 清空关键词后显示错误的界面状态(🔴 高优先级)
- 模板条件判断逻辑不清晰(🟡 中优先级)
- 切换分类时的状态不一致(🟡 中优先级)
- 实时搜索 vs 手动搜索冲突(🟢 低优先级)
推荐行动
- 立即修复:问题 1(使用方案 1)
- 考虑重构:问题 2(使用方案 2)
- 验证测试:确保所有测试场景通过
- 用户测试:邀请真实用户测试搜索流程
生成时间: 2026-01-31 分析工具: Claude Code 测试文件: index.test.js