如何在Next.js中通过搜索栏调用本地API按搜索词查询商品标题
Next.js 商品标题检索功能正确实现方案
原有代码问题汇总
- API路由逻辑错误:本地已经导入商品数据源,反而发起自调用fetch请求,且错误将整个query对象拼接为路径参数,无法正确读取搜索关键词
- 前端请求格式错误:URL参数写为
?=,缺少参数名,搜索词无法被后端接收 - 缺少核心匹配逻辑:没有实现标题关键词模糊匹配的过滤规则
- 缺少性能优化:输入时直接触发请求,没有防抖控制,会产生大量无效请求
- 缺少结果状态:请求拿到数据后没有存储,无法渲染搜索结果
修正后的API路由代码
文件路径:pages/api/products.js
import { products } from './dataproducts' export default async function handler(req, res) { const { search } = req.query // 未传入搜索关键词时返回全量商品 if (!search?.trim()) { return res.status(200).json(products) } // 大小写不敏感的模糊匹配 const keyword = search.trim().toLowerCase() const matchList = products.filter(item => item.title.toLowerCase().includes(keyword)) res.status(200).json(matchList) }
修正后的搜索栏组件代码
文件路径:components/SearchBar.js
import { useState, useEffect } from 'react' import styles from '../../styles/SearchBar.module.css' function SearchBar() { const [query, setQuery] = useState('') const [resultList, setResultList] = useState([]) const [debounceTimer, setDebounceTimer] = useState(null) const handleInput = (e) => { const inputVal = e.target.value setQuery(inputVal) // 清除上一次的请求定时器 if (debounceTimer) clearTimeout(debounceTimer) // 输入长度不足时清空结果,不发起请求 if (inputVal.trim().length <= 3) { setResultList([]) return } // 输入停止300ms后再发起请求,减少无效调用 const timer = setTimeout(async () => { try { const res = await fetch(`http://localhost:3000/api/products?search=${encodeURIComponent(inputVal.trim())}`) const data = await res.json() setResultList(data) } catch (err) { console.error('搜索失败:', err) setResultList([]) } }, 300) setDebounceTimer(timer) } // 组件卸载时清除定时器,避免内存泄漏 useEffect(() => { return () => debounceTimer && clearTimeout(debounceTimer) }, [debounceTimer]) return ( <div className={styles.searchContainer}> <input className={styles.input} onChange={handleInput} id='productSearch' type="search" name="search" value={query} placeholder="Nombre producto..." /> {/* 搜索结果列表 */} {resultList.length > 0 && ( <ul className={styles.resultBox}> {resultList.map(product => ( <li key={product.id} className={styles.resultItem}> <img src={product.image} alt={product.title} width={48} height={48} /> <div> <p className={styles.productTitle}>{product.title}</p> <p className={styles.productPrice}>€{product.price}</p> </div> </li> ))} </ul> )} {/* 无匹配结果提示 */} {query.trim().length > 3 && resultList.length === 0 && ( <div className={styles.emptyTip}>No se encontraron productos coincidentes</div> )} </div> ) } export default SearchBar
关键逻辑说明
- API层直接基于本地数据源做过滤,去掉冗余的自调用fetch,响应速度更快
- 前后端统一使用
search作为参数名,修正URL参数格式错误,搜索词可正常传递 - 匹配逻辑统一转小写后做包含判断,支持大小写不敏感的模糊检索
- 加入输入防抖机制,用户连续输入时不会重复发起请求,降低服务端压力
- 补全结果状态管理,支持匹配结果渲染、空结果提示,搜索交互完整可用
- 关键词做URL编码处理,避免特殊字符导致接口请求异常
内容的提问来源于stack exchange,提问作者Francisco José López Laperal
相关产品推荐
相关产品推荐

