前端实战:基于 localStorage 实现无后端搜索历史
搜索历史记录是电商、文档检索等场景的基础功能。在未登录或无需后端介入的场景下,仅利用前端 localStorage 即可实现关键词自动记录、刷新不丢失、重复置顶、单条删除及一键清空等核心需求。
完整代码示例
以下是一个完整的 HTML/CSS/JS 实现方案:
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>localStorage 搜索历史示例</title><style>* {box-sizing: border-box;}body {font-family: system-ui, sans-serif;max-width: 600px;margin: 40px auto;padding: 0 20px;}.search-wrap {display: flex;gap: 8px;margin-bottom: 20px;}#searchInput {flex: 1;padding: 10px 12px;font-size: 16px;border: 1px solid #ccc;border-radius: 6px;}button {border: none;border-radius: 6px;cursor: pointer;background: #2563eb;color: #fff;}#searchBtn {padding: 10px 16px;}button:hover {background: #1d4ed8;}.history-block {display: none;}.history-block.show {display: block;}.history-header {display: flex;justify-content: space-between;align-items: center;}.history-header h4 {margin: 0;font-size: 15px;font-weight: 600;}#clearBtn {background: transparent;color: #666;padding: 0;font-size: 13px;}#clearBtn:hover {background: transparent;color: #333;}#historyBox {margin-top:14px;}.history-item {display: inline-flex;align-items: center;background: #f3f4f6;padding: 4px 8px;border-radius: 16px;margin: 4px 6px 4px 0;}.history-item .word {margin-right: 6px;font-size: 14px;}.history-item .close {background: transparent;color: #888;width: 14px;height: 14px;padding: 0;font-size: 12px;line-height: 1;display: inline-flex;align-items: center;justify-content: center;cursor: pointer;}.history-item .close:hover {background: transparent;color: #333;}</style></head><body><div class="search-wrap"><input id="searchInput" type="text" placeholder="输入搜索词,回车或点击搜索"><button id="searchBtn">搜索</button></div><div class="history-block" id="historyBlock"><div class="history-header"><h4>搜索历史</h4><button id="clearBtn">清空</button></div><div id="historyBox"></div></div><script>const STORAGE_KEY = 'search_history'const MAX_COUNT = 15function escapeHtml(str) {return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"g, '"').replace(/'/g, ''')}function getHistory() {try {const str = localStorage.getItem(STORAGE_KEY)return str ? JSON.parse(str) : []} catch (e) {console.error('读取历史出错', e)return []}}function saveHistory(keyword) {const kw = keyword.trim()if (!kw) returnlet list = getHistory()list = list.filter(item => item !== kw)list.unshift(kw)if (list.length > MAX_COUNT) {list = list.slice(0, MAX_COUNT)}try {localStorage.setItem(STORAGE_KEY, JSON.stringify(list))} catch (err) {console.warn('localStorage 存储空间不足', err)}renderHistory()}function removeItem(word) {let list = getHistory()list = list.filter(item => item !== word)localStorage.setItem(STORAGE_KEY, JSON.stringify(list))renderHistory()}function clearHistory() {localStorage.removeItem(STORAGE_KEY)renderHistory()}function renderHistory() {const block = document.getElementById('historyBlock')const box = document.getElementById('historyBox')const list = getHistory()if (list.length === 0) {block.classList.remove('show')box.innerHTML = ''return}block.classList.add('show')let html = ''list.forEach(item => {const safeWord = escapeHtml(item)html += `<div class="history-item"><span class="word">${safeWord}</span><button class="close" data-word="${safeWord}" type="button">×</button></div>`})box.innerHTML = html}document.getElementById('searchBtn').addEventListener('click', () => {const input = document.getElementById('searchInput')saveHistory(input.value)input.value = ''})document.getElementById('searchInput').addEventListener('keydown', (e) => {if (e.key === 'Enter') {const input = document.getElementById('searchInput')saveHistory(input.value)input.value = ''}})document.getElementById('clearBtn').addEventListener('click', clearHistory)document.getElementById('historyBox').addEventListener('click', (e) => {if (e.target.classList.contains('close')) {removeItem(e.target.dataset.word)}})window.addEventListener('storage', (e) => {if (e.key === STORAGE_KEY) {renderHistory()}})renderHistory()</script></body></html>
存储规则解析
由于 localStorage 仅支持字符串存储,本方案通过 JSON.stringify 和 JSON.parse 实现数组的序列化与反序列化。数据结构采用简单的字符串数组,轻量且高效:
["前端面试", "localStorage 使用", "JS 实战"]
核心业务逻辑
- 无效过滤:搜索内容为空或纯空格时,直接拦截不存储。
- 去重置顶若关键词已存在,先移除旧数据,再将新数据插入数组头部。
- 数量限制:当记录数超过设定阈值(如 15 条)时,自动截断数组,仅保留最新的 N 条。
- 视图同步:所有数据操作完成后,立即触发页面重新渲染,确保 UI 与数据状态一致。
搜索历史功能看似简单,但要实现健壮、优雅且无 Bug 的体验,需综合考量存储逻辑、去重算法、异常容错、XSS 安全防护及交互细节。本套基于 localStorage 的实现方案零成本、高可用,是前端开发者必备的基础技能。未来若需对接后端或多端同步,只需在此架构上增加接口请求与数据合并逻辑即可无缝拓展。
#搜索历史 #localStorage

