使用UniApp实现个性化的搜索框与搜索历史记录
在移动端应用开发中,搜索功能几乎是标配,而一个好的搜索体验不仅仅是功能的实现,更是用户留存的关键。本文将分享如何在UniApp框架下打造一个既美观又实用的搜索框,并实现搜索历史记录功能。
前言
做了这么多年的前端开发,发现搜索功能看似简单,但要做好却不容易。特别是在跨端开发框架UniApp中,既要考虑不同平台的兼容性,又要保证用户体验的一致性。最近在一个电商项目中,我重新设计了搜索模块,今天就把这部分经验分享给大家。
需求分析
首先明确我们要实现的功能:
- 一个美观的搜索框,支持输入搜索关键词
- 实时显示搜索建议(热门搜索)
- 记录用户的搜索历史,并提供查看和清除功能
- 点击历史记录可以快速进行搜索
这些看似常见的功能,实现起来却有不少细节需要注意。接下来,我们一步步来实现。
基础界面搭建
首先,我们创建一个搜索页面search.vue
:
vue
<template>
<view class="search-container">
<!-- 搜索框部分 -->
<view class="search-header">
<view class="search-box">
<text class="iconfont icon-search"></text>
<input
type="text"
v-model="keyword"
confirm-type="search"
placeholder="请输入搜索关键词"
@confirm="handleSearch"
@input="handleInput"
/>
<text v-if="keyword" class="iconfont icon-close" @tap="clearKeyword"></text>
</view>
<text class="cancel-btn" @tap="goBack">取消</text>
</view>
<!-- 内容区域 -->
<view class="search-content">
<!-- 搜索历史 -->
<view class="history-section" v-if="searchHistory.length > 0">
<view class="section-header">
<text>搜索历史</text>
<text class="iconfont icon-delete" @tap="clearHistory"></text>
</view>
<view class="tag-list">
<view
class="tag-item"
v-for="(item, index) in searchHistory"
:key="index"
@tap="searchByTag(item)"
>
{{item}}
</view>
</view>
</view>
<!-- 热门搜索 -->
<view class="hot-section">
<view class="section-header">
<text>热门搜索</text>
</view>
<view class="tag-list">
<view
class="tag-item"
v-for="(item, index) in hotKeywords"
:key="index"
@tap="searchByTag(item)"
>
{{item}}
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
keyword: '',
searchHistory: [],
hotKeywords: ['手机', '电脑', '耳机', '平板', '相机', '手表', '家电', '配件']
}
},
onLoad() {
// 页面加载时获取历史记录
this.getSearchHistory()
},
methods: {
handleSearch() {
if (!this.keyword.trim()) return
// 保存搜索记录
this.saveSearchHistory(this.keyword)
// 执行搜索跳转
this.navigateToResult(this.keyword)
},
searchByTag(keyword) {
this.keyword = keyword
this.handleSearch()
},
clearKeyword() {
this.keyword = ''
},
goBack() {
uni.navigateBack()
},
handleInput() {
// 可以在这里实现输入联想功能
},
getSearchHistory() {
const history = uni.getStorageSync('searchHistory')
if (history) {
this.searchHistory = JSON.parse(history)
}
},
saveSearchHistory(keyword) {
let history = [...this.searchHistory]
// 如果已存在相同关键词,先移除
const index = history.findIndex(item => item === keyword)
if (index !== -1) {
history.splice(index, 1)
}
// 将新关键词添加到头部
history.unshift(keyword)
// 只保留最近10条记录
if (history.length > 10) {
history = history.slice(0, 10)
}
this.searchHistory = history
uni.setStorageSync('searchHistory', JSON.stringify(history))
},
clearHistory() {
uni.showModal({
title: '提示',
content: '确定要清空搜索历史吗?',
success: res => {
if (res.confirm) {
this.searchHistory = []
uni.removeStorageSync('searchHistory')
}
}
})
},
navigateToResult(keyword) {
uni.navigateTo({
url: `/pages/search-result/search-result?keyword=${encodeURIComponent(keyword)}`
})
}
}
}
</script>
<style lang="scss">
.search-container {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f8f8f8;
.search-header {
display: flex;
align-items: center;
padding: 20rpx 30rpx;
background-color: #ffffff;
.search-box {
flex: 1;
display: flex;
align-items: center;
height: 70rpx;
background-color: #f5f5f5;
border-radius: 35rpx;
padding: 0 20rpx;
.iconfont {
font-size: 36rpx;
color: #999;
}
input {
flex: 1;
height: 100%;
margin: 0 15rpx;
font-size: 28rpx;
}
.icon-close {
padding: 10rpx;
}
}
.cancel-btn {
margin-left: 20rpx;
font-size: 28rpx;
color: #333;
}
}
.search-content {
flex: 1;
padding: 30rpx;
.section-header {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
font-size: 30rpx;
color: #333;
.iconfont {
font-size: 32rpx;
color: #999;
}
}
.tag-list {
display: flex;
flex-wrap: wrap;
.tag-item {
padding: 10rpx 30rpx;
margin: 0 20rpx 20rpx 0;
background-color: #ffffff;
border-radius: 30rpx;
font-size: 26rpx;
color: #666;
}
}
.history-section {
margin-bottom: 50rpx;
}
}
}
</style>
实现搜索历史功能
在上面的代码中,我们已经基本实现了搜索历史的保存和展示功能。整个实现思路是:
- 在用户进行搜索时,通过
saveSearchHistory
方法将关键词保存到本地存储 - 页面加载时,通过
getSearchHistory
从本地读取历史记录 - 点击历史记录标签可以快速发起搜索
- 提供清空历史记录的功能
但是,这里还有一些优化点,比如:
1. 防止重复保存
当用户多次搜索相同关键词时,我们不应该重复保存。在代码中,我们先查找是否已存在相同关键词,如果存在则先移除,然后将新的关键词添加到列表头部,这样可以保证最近搜索的内容总是排在最前面。
2. 限制历史记录数量
为了避免占用过多存储空间,我们限制只保留最近的10条搜索记录:
js
// 只保留最近10条记录
if (history.length > 10) {
history = history.slice(0, 10)
}
进阶:实现搜索建议功能
现在,我们的搜索框基本功能已经实现了,但是为了提升用户体验,我们可以增加实时搜索建议功能。当用户输入关键词时,后台可以返回相关的搜索建议。
vue
<template>
<!-- 搜索建议列表 -->
<view class="suggestion-list" v-if="keyword && suggestions.length > 0">
<view
class="suggestion-item"
v-for="(item, index) in suggestions"
:key="index"
@tap="searchByTag(item)"
>
<text class="iconfont icon-search"></text>
<text>{{item}}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// ...现有代码
suggestions: []
}
},
methods: {
// ...现有代码
// 修改handleInput方法
handleInput() {
if (!this.keyword.trim()) {
this.suggestions = []
return
}
// 这里模拟从服务端获取搜索建议
// 实际开发中,应该调用API获取数据
setTimeout(() => {
if (this.keyword) {
this.suggestions = this.mockSuggestions(this.keyword)
}
}, 300)
},
mockSuggestions(keyword) {
// 这只是一个模拟的建议列表,实际开发中应该调用API
const allSuggestions = {
'手': ['手机', '手表', '手机壳', '手持云台'],
'电': ['电脑', '电视', '电饭煲', '电动牙刷'],
'耳': ['耳机', '耳麦', '耳温枪']
}
for (const key in allSuggestions) {
if (keyword.includes(key)) {
return allSuggestions[key]
}
}
return []
}
}
}
</script>
<style lang="scss">
// 添加搜索建议样式
.suggestion-list {
position: absolute;
top: 110rpx;
left: 0;
right: 0;
background-color: #fff;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
z-index: 999;
.suggestion-item {
display: flex;
align-items: center;
padding: 20rpx 30rpx;
border-bottom: 1px solid #f5f5f5;
.iconfont {
font-size: 32rpx;
color: #999;
margin-right: 15rpx;
}
text {
font-size: 28rpx;
color: #333;
}
}
}
</style>
防抖处理
为了避免频繁请求服务器,我们可以对输入事件进行防抖处理:
js
// 需要先定义一个定时器变量
data() {
return {
// ...现有数据
debounceTimer: null
}
},
// 修改handleInput方法
handleInput() {
if (!this.keyword.trim()) {
this.suggestions = []
return
}
// 清除之前的定时器
clearTimeout(this.debounceTimer)
// 设置新的定时器
this.debounceTimer = setTimeout(() => {
// 调用API获取搜索建议
this.getSuggestions()
}, 300)
},
// 获取搜索建议的方法
getSuggestions() {
// 实际开发中应该调用API
// 这里使用模拟数据
this.suggestions = this.mockSuggestions(this.keyword)
// 真实API调用可能是这样:
/*
uni.request({
url: 'https://your-api.com/suggestions',
data: {
keyword: this.keyword
},
success: (res) => {
this.suggestions = res.data.suggestions || []
}
})
*/
}
实战案例:电商搜索页面
在实际的电商项目中,我们不仅需要基础的搜索功能,还需要分类筛选、排序等高级功能。以下是一个简化的电商搜索结果页面:
vue
<!-- search-result.vue -->
<template>
<view class="result-container">
<!-- 搜索框 -->
<view class="search-bar">
<view class="search-box" @tap="goToSearch">
<text class="iconfont icon-search"></text>
<text class="placeholder">{{keyword || '请输入搜索关键词'}}</text>
</view>
</view>
<!-- 筛选条件 -->
<view class="filter-bar">
<view
class="filter-item"
:class="{active: currentSort === 'default'}"
@tap="changeSort('default')"
>
综合
</view>
<view
class="filter-item"
:class="{active: currentSort === 'sales'}"
@tap="changeSort('sales')"
>
销量
</view>
<view
class="filter-item"
:class="{active: currentSort === 'price'}"
@tap="changeSort('price')"
>
价格
<text class="iconfont" :class="priceOrder === 'asc' ? 'icon-up' : 'icon-down'"></text>
</view>
<view
class="filter-item"
:class="{active: showFilter}"
@tap="toggleFilter"
>
筛选
<text class="iconfont icon-filter"></text>
</view>
</view>
<!-- 商品列表 -->
<view class="goods-list">
<view
class="goods-item"
v-for="(item, index) in goodsList"
:key="index"
@tap="goToDetail(item.id)"
>
<image :src="item.image" mode="aspectFill"></image>
<view class="goods-info">
<view class="goods-title">{{item.title}}</view>
<view class="goods-price">¥{{item.price}}</view>
<view class="goods-extra">
<text class="sales">销量 {{item.sales}}</text>
<text class="comment">评价 {{item.comment}}</text>
</view>
</view>
</view>
</view>
<!-- 无结果提示 -->
<view class="empty-tip" v-if="goodsList.length === 0">
<image src="/static/images/empty.png"></image>
<text>没有找到相关商品</text>
</view>
</view>
</template>
总结与优化建议
通过本文,我们实现了一个功能完善的搜索框和搜索历史记录功能。但在实际应用中,还可以做以下优化:
- 搜索框自动聚焦:页面加载时,自动让输入框获得焦点,提高用户体验
- 历史记录去重:除了当前的去重逻辑,还可以考虑对相似关键词进行合并
- 个性化推荐:根据用户的历史搜索记录,提供个性化的搜索推荐
- 搜索结果缓存:对热门搜索结果进行缓存,提高加载速度
- 语音搜索:集成语音识别API,支持语音搜索功能
- 图片搜索:支持上传图片进行搜索,特别适用于电商场景
最后的思考
在移动端应用中,搜索功能是连接用户与内容的桥梁。一个好的搜索体验,能够大大提升用户对应用的满意度。因此,我们不仅要关注功能的实现,还要从用户体验的角度出发,打造更加人性化的搜索功能。
希望本文能对你在UniApp中实现搜索功能有所帮助。如果有任何问题或建议,欢迎在评论区交流讨论!