- 云开发,实现【最近搜索】和【大家在搜】的存储和读取逻辑
小程序【搜索】功能是很常见的,在开发公司的一个电商小程序的时候想尝试使用“云开发”做系统的后端,而首页顶端就有个搜索框,所以第一步想先解决【搜索】的一些前期工作:【最近搜索】和【大家在搜】关键词的存储和读取逻辑。 效果图如下: [图片] 效果视频请点击链接查看: https://v.vuevideo.net/share/post/-2263996935468719531 或者微信扫码观看: [图片] 为什么需要这两个功能? 【最近搜索】:可以帮助用户快速选择历史搜索记录(我这里只保存10个),搜索相同内容时减少打字操作,更加人性化; 【大家在搜】:这部分的数据可以是从所有用户的海量搜索中提取的前n名,也可以是运营者想给用户推荐的商品关键词,前者是真实的“大家在搜”,后者更像是一种推广。 具体实现 流程图: [图片] 可以结合效果图看流程图,用户触发操作有三种形式: 输入关键词,点击搜索按钮; 点击【大家在搜】列举的关键词; 点击【最近搜索】的关键词 这里发现1和2是一样的逻辑,而3更简单,所以把1和2归为一类(左边流程图),3单独一类(右边流程图) 两个流程图里都含有相同的片段(图中红色背景块部分):“找出这条记录”->“更新其时间”。故这部分可以封装成函数:updateTimeStamp()。 更新时间指的是更新该条记录的时间戳,每条记录包含以下字段:_id、keyword、openid、timeStamp。其中_id是自动生成的,openid是当前用户唯一标识,意味着关键词记录与人是绑定的,每个用户只能看到自己的搜索记录,timeStamp是触发搜索动作时的时间戳,记录时间是为了排序,即最近搜索的排在最前面。 代码结构: [图片] 云函数: cloudfunctions / searchHistory / index.js [代码]const cloud = require('wx-server-sdk') cloud.init() const db = cloud.database() exports.main = async (event, context) => { try { switch (event.type) { // 根据openid获取记录 case 'getByOpenid': return await db.collection('searchHistory').where({ openid: event.openid }).orderBy('timeStamp', 'desc').limit(10).get() break // 添加记录 case 'add': return await db.collection('searchHistory').add({ // data 字段表示需新增的 JSON 数据 data: { openid: event.openid, timeStamp: event.timeStamp, keyword: event.keyword } }) break // 根据openid和keyword找出记录,并更新其时间戳 case 'updateOfOpenidKeyword': return await db.collection('searchHistory').where({ openid: event.openid, keyword: event.keyword }).update({ data: { timeStamp: event.timeStamp } }) break // 根据openid和keyword能否找出这条记录(count是否大于0) case 'canFindIt': return await db.collection('searchHistory').where({ openid: event.openid, keyword: event.keyword }).count() break // 根据openid查询当前记录条数 case 'countOfOpenid': return await db.collection('searchHistory').where({ openid: event.openid }).count() break // 找出该openid下最早的一条记录 case 'getEarliestOfOpenid': return await db.collection('searchHistory').where({ openid: event.openid }).orderBy('timeStamp', 'asc').limit(1).get() break // 根据最早记录的id,删除这条记录 case 'removeOfId': return await db.collection('searchHistory').where({ _id: event._id }).remove() break // 删除该openid下的所有记录 case 'removeAllOfOpenid': return await db.collection('searchHistory').where({ openid: event.openid }).remove() break } } catch (e) { console.error('云函数【searchHistory】报错!!!', e) } } [代码] cloudfunctions / recommendedKeywords / index.js [代码]const cloud = require('wx-server-sdk') cloud.init() const db = cloud.database() exports.main = async (event, context) => await db.collection('recommendedKeywords') .orderBy('level', 'asc') .limit(10) .get() [代码] cloudfunctions / removeExpiredSearchHistory / config.json [代码]// 该定时触发器被设置成每天晚上23:00执行一次 index.js (删除3天前的数据) { "triggers": [ { "name": "remove expired search history", "type": "timer", "config": "0 0 23 * * * *" } ] } [代码] cloudfunctions / removeExpiredSearchHistory / index.js [代码]const cloud = require('wx-server-sdk') cloud.init() const db = cloud.database() const _ = db.command let timeStamp = new Date().getTime() // 删除3天前的数据 let duration = timeStamp - 1000 * 60 * 60 * 24 * 3 exports.main = async (event, context) => { try { let arr = await db.collection('searchHistory').where({ timeStamp: _.lt(duration) }).get() let idArr = arr.data.map(v => v._id) console.log('idArr=', idArr) for (let i = 0; i < idArr.length; i++) { await db.collection('searchHistory').where({ _id: idArr[i] }).remove() } } catch (e) { console.error(e) } } [代码] search.js 关键代码: // 输入关键词,点击搜索 [代码] tapSearch: function () { let that = this if (that.data.openid) { // 只为已登录的用户记录搜索历史 that.tapSearchOrRecommended(that.data.keyword, that.data.openid) } else { // 游客直接跳转 wx.navigateTo({ url: `../goods-list/goods-list?keyword=${that.data.keyword}`, }) } }, [代码] // 点击推荐的关键词 [代码]tabRecommended: function (e) { let that = this let keyword = e.currentTarget.dataset.keyword that.setData({ keyword }) if (that.data.openid) { // 只为已登录的用户记录搜索历史 that.tapSearchOrRecommended(keyword, that.data.openid) } else { // 游客直接跳转 wx.navigateTo({ url: `../goods-list/goods-list?keyword=${keyword}`, }) } }, [代码] // 点击历史关键词 [代码]tabHistory: function (e) { let that = this let keyword = e.currentTarget.dataset.keyword wx.navigateTo({ url: `../goods-list/goods-list?keyword=${keyword}`, }) that.updateTimeStamp(keyword, that.data.openid) }, [代码] // 获取历史记录和推荐 [代码]getHistoryAndRecommended: function () { let that = this try { // 只为已登录的用户获取搜索历史 if (that.data.openid) { let searchHistoryNeedUpdate = app.globalData.searchHistoryNeedUpdate const searchHistory = wx.getStorageSync('searchHistory') // 如果本地有历史并且还没有更新的历史 if (searchHistory && !searchHistoryNeedUpdate) { console.log('本地有搜索历史,暂时不需要更新') that.setData({ searchHistory }) } else { console.log('需要更新(或者本地没有搜索历史)') wx.cloud.callFunction({ name: 'searchHistory', data: { type: 'getByOpenid', openid: that.data.openid }, success(res) { console.log('云函数获取关键词记录成功') let searchHistory = [] for (let i = 0; i < res.result.data.length; i++) { searchHistory.push(res.result.data[i].keyword) } that.setData({ searchHistory }) wx.setStorage({ key: 'searchHistory', data: searchHistory, success(res) { console.log('searchHistory本地存储成功') // wx.stopPullDownRefresh() app.globalData.searchHistoryNeedUpdate = false }, fail: console.error }) }, fail: console.error }) } } // 获取推荐关键词 wx.cloud.callFunction({ name: 'recommendedKeywords', success(res) { console.log('云函数获取推荐关键词记录成功') let recommendedKeywords = [] for (let i = 0; i < res.result.data.length; i++) { recommendedKeywords.push(res.result.data[i].keyword) } that.setData({ recommendedKeywords }) }, fail: console.error }) } catch (e) { fail: console.error } }, [代码] // 添加该条新记录(tapSearchOrRecommended()要用到它两次) [代码]addRecord: function (keyword, timeStamp) { let that = this // 【添加】 wx.cloud.callFunction({ name: 'searchHistory', data: { type: 'add', openid: that.data.openid, keyword, timeStamp }, success(res) { console.log('云函数添加关键词成功') app.globalData.searchHistoryNeedUpdate = true }, fail: console.error }) }, [代码] // 根据openid和keyword找出记录,并更新其时间戳 [代码]updateTimeStamp: function (keyword, openid) { this.setData({ keyword }) let timeStamp = new Date().getTime() wx.cloud.callFunction({ name: 'searchHistory', data: { type: 'updateOfOpenidKeyword', openid, keyword, timeStamp, }, success(res) { console.log('云函数更新关键词时间戳成功') app.globalData.searchHistoryNeedUpdate = true }, fail: console.error }) }, [代码] // 输入关键词,点击搜索或者点击推荐的关键词 [代码]tapSearchOrRecommended: function (keyword, openid) { let that = this if (!keyword) { wx.showToast({ icon: 'none', title: '请输入商品关键词', }) setTimeout(function () { that.setData({ isFocus: true }) }, 1500) return false } wx.navigateTo({ url: `../goods-list/goods-list?keyword=${keyword}`, }) let timeStamp = new Date().getTime() // 【根据openid和keyword能否找出这条记录(count是否大于0)】 wx.cloud.callFunction({ name: 'searchHistory', data: { type: 'canFindIt', openid, keyword }, success(res) { console.log('res.result.total=', res.result.total) if (res.result.total === 0) { // 集合中没有 // 【根据openid查询当前记录条数】 wx.cloud.callFunction({ name: 'searchHistory', data: { type: 'countOfOpenid', openid }, success(res) { // 记录少于10条 if (res.result.total < 10) { // 【添加】 that.addRecord(keyword, timeStamp) } else { // 【找出该openid下最早的一条记录】 wx.cloud.callFunction({ name: 'searchHistory', data: { type: 'getEarliestOfOpenid', openid }, success(res) { console.log('云函数找出最早的一条关键词成功', res.result.data[0]) let _id = res.result.data[0]._id // 【根据最早记录的id,删除这条记录】 wx.cloud.callFunction({ name: 'searchHistory', data: { type: 'removeOfId', _id }, success(res) { console.log('云函数删除最早的一条关键词成功') // 【添加】 that.addRecord(keyword, timeStamp) }, fail: console.error }) }, fail: console.error }) } }, fail: console.error }) } else { // 【根据openid和keyword找出记录,并更新其时间戳】 that.updateTimeStamp(keyword, openid) } }, fail: console.error }) } [代码]
2019-03-28 - 小程序中使用防抖函数
这几天看了很多关于防抖函数的博客,我是在微信小程序中使用,在此总结一下关于防抖函数的知识。 为什么需要防抖函数? 防抖函数适用的是【有大量重复操作】的场景,比如列表渲染之后对每一项进行操作。 函数代码: [代码]var timer; debounce: function (func, wait) { return () => { clearTimeout(timer); timer = setTimeout(func, wait); }; }, [代码] 参数: func:需要防抖的函数; wait:number类型,setTimeout的时间参数; 代码分析: 命名一个叫做debounce的函数,参数有两个(func,wait),return一个函数,内容为清除计时器,然后设置计时器,计时器的意思是:在wait时间后执行func。 清除计时器是整个函数的核心,因为防抖需要不停地清除计时器,最后在计时器结束后触发func来达到效果。 防抖函数的调用方法 example: [代码]this.debounce(this.函数名,3000)() [代码] 在使用这个函数的时候我遇到了一些问题: 因为微信小程序中很多地方都需要使用this.setData,如果对于this指向的理解不深入的话,很容易出现以下情况: 1:this==undefined; 2:Error:data is not defined; 等等一些列关于this的问题。 解决方法: [代码]this.debounce(this.函数名.bind(this),3000)() [代码] 使用bind(this)把this指向传到函数内部就解决了。
2019-03-19