- 用150行代码实现Vuex 80%的功能
作者: 殷荣桧@腾讯 本文地址,欢迎查看 本文github仓库代码地址,欢迎star,谢谢。 如果你对自己用少量代码实现各个框架感兴趣,那下面这些你都可以一看: build-your-own-react build-your-own-flux build-your-own-redux 目录: 一.完成最简单的通过vuex定义全局变量,在任何一个页面可以通过this.$store.state.count可以直接使用 二.vuex中的getter方法的实现 三.mutation和commit方法的实现 四.actions和dispatch方法的实现 五.module方法的实现 六.实现:Vue.use(Vuex) 先来看一下用自己实现的的vuex替代真实的vuex的效果,看看能否正常运行,有没有报错: [图片] 从运行结果来看,运行正常,没有问题。接下来看看一步一步实现的过程: <h4>一. 完成最简单的通过vuex定义全局变量,在任何一个页面可以通过this.$store.state.count可以直接使用</h4> main.js代码如下: [代码]let store = new Vuex.Store({ state: { count: 0 } }, Vue); new Vue({ store, render: h => h(App), }).$mount('#app') [代码] store.js的代码如下: [代码]export class Store { constructor(options = {}, Vue) { this.options = options; Vue.mixin({ beforeCreate: vuexInit }); } get state () { return this.options.state; } } function vuexInit () { const options = this.$options if (options.store) { // 组件内部设定了store,则优先使用组件内部的store this.$store = typeof options.store === 'function' ? options.store() : options.store } else if (options.parent && options.parent.$store) { // 组件内部没有设定store,则从根App.vue下继承$store方法 this.$store = options.parent.$store } } [代码] 界面代码如下: [代码]<script> export default { name: 'app', created() { console.log('打印出this.$store.state.count的结果',this.$store.state.count); }, } </script> [代码] 运行结果: 成功打印出this.$store.state.count的值为0 <h4>二. vuex中的getter方法的实现</h4> main.js代码如下: [代码]let store = new Vuex.Store({ state: { count: 0 }, getters: { getStatePlusOne(state) { return state.count + 1 } } }, Vue); new Vue({ store, render: h => h(App), }).$mount('#app') [代码] store.js的代码如下: [代码]export class Store { constructor(options = {}, Vue) { this.options = options; this.getters = {} Vue.mixin({ beforeCreate: vuexInit }); forEachValue(options.getters, (getterFn, getterName) => { registerGetter(this, getterName, getterFn); }) } get state() { return this.options.state; } } function registerGetter(store, getterName, getterFn) { Object.defineProperty(store.getters, getterName, { get: () => { return getterFn(store.state) } }) } // 将对象中的每一个值放入到传入的函数中作为参数执行 function forEachValue(obj, fn) { Object.keys(obj).forEach(key => fn(obj[key], key)); } function vuexInit() { const options = this.$options if (options.store) { // 组件内部设定了store,则优先使用组件内部的store this.$store = typeof options.store === 'function' ? options.store() : options.store } else if (options.parent && options.parent.$store) { // 组件内部没有设定store,则从根App.vue下继承$store方法 this.$store = options.parent.$store } } [代码] 界面代码如下: [代码]<script> export default { name: 'app', created() { console.log('打印出this.$store.getters.getStatePlusOne的结果',this.$store.getters.getStatePlusOne); }, } </script> [代码] 运行结果: 成功打印出this.$store.getters.getStatePlusOne的值为1 <h4>三. mutation和commit方法的实现</h4> main.js代码如下: [代码]let store = new Vuex.Store({ state: { count: 0 }, mutations: { incrementFive(state) { // console.log('初始state', JSON.stringify(state)); state.count = state.count + 5; } }, getters: { getStatePlusOne(state) { return state.count + 1 } } }, Vue); [代码] store.js的代码如下: [代码]export class Store { constructor(options = {}, Vue) { Vue.mixin({ beforeCreate: vuexInit }) this.options = options; this.getters = {}; this.mutations = {}; const { commit } = this; this.commit = (type) => { return commit.call(this, type); } forEachValue(options.getters, (getterFn, getterName) => { registerGetter(this, getterName, getterFn); }); forEachValue(options.mutations, (mutationFn, mutationName) => { registerMutation(this, mutationName, mutationFn) }); this._vm = new Vue({ data: { state: options.state } }); } get state() { // return this.options.state; // 无法完成页面中的双向绑定,所以改用this._vm的形式 return this._vm._data.state; } commit(type) { this.mutations[type](); } } function registerMutation(store, mutationName, mutationFn) { store.mutations[mutationName] = () => { mutationFn.call(store, store.state); } } function registerGetter(store, getterName, getterFn) { Object.defineProperty(store.getters, getterName, { get: () => { return getterFn(store.state) } }) } // 将对象中的每一个值放入到传入的函数中作为参数执行 function forEachValue(obj, fn) { Object.keys(obj).forEach(key => fn(obj[key], key)); } function vuexInit() { const options = this.$options if (options.store) { // 组件内部设定了store,则优先使用组件内部的store this.$store = typeof options.store === 'function' ? options.store() : options.store } else if (options.parent && options.parent.$store) { // 组件内部没有设定store,则从根App.vue下继承$store方法 this.$store = options.parent.$store } } [代码] 界面代码如下: [代码]<script> export default { name: 'app', created() { console.log('打印出this.$store.getters.getStatePlusOne的结果',this.$store.getters.getStatePlusOne); }, mounted() { setTimeout(() => { this.$store.commit('incrementFive'); console.log('store state自增5后的结果', this.$store.state.count); }, 2000); }, computed: { count() { return this.$store.state.count; } } } </script> [代码] 运行结果:成功在2秒之后输出count自增5后的结果5 <h4>四. actions和dispatch方法的实现</h4> main.js代码如下: [代码]let store = new Vuex.Store({ state: { count: 0 }, actions: { countPlusSix(context) { context.commit('plusSix'); } }, mutations: { incrementFive(state) { // console.log('初始state', JSON.stringify(state)); state.count = state.count + 5; }, plusSix(state) { state.count = state.count + 6; } }, getters: { getStatePlusOne(state) { return state.count + 1 } } }, Vue); [代码] store.js的代码如下: [代码]export class Store { constructor(options = {}, Vue) { Vue.mixin({ beforeCreate: vuexInit }) this.options = options; this.getters = {}; this.mutations = {}; this.actions = {}; const { dispatch, commit } = this; this.commit = (type) => { return commit.call(this, type); } this.dispatch = (type) => { return dispatch.call(this, type); } forEachValue(options.actions, (actionFn, actionName) => { registerAction(this, actionName, actionFn); }); forEachValue(options.getters, (getterFn, getterName) => { registerGetter(this, getterName, getterFn); }); forEachValue(options.mutations, (mutationFn, mutationName) => { registerMutation(this, mutationName, mutationFn) }); this._vm = new Vue({ data: { state: options.state } }); } get state() { // return this.options.state; // 无法完成页面中的双向绑定,所以改用this._vm的形式 return this._vm._data.state; } commit(type) { this.mutations[type](); } dispatch(type) { return this.actions[type](); } } function registerMutation(store, mutationName, mutationFn) { store.mutations[mutationName] = () => { mutationFn.call(store, store.state); } } function registerAction(store, actionName, actionFn) { store.actions[actionName] = () => { actionFn.call(store, store) } } function registerGetter(store, getterName, getterFn) { Object.defineProperty(store.getters, getterName, { get: () => { return getterFn(store.state) } }) } // 将对象中的每一个值放入到传入的函数中作为参数执行 function forEachValue(obj, fn) { Object.keys(obj).forEach(key => fn(obj[key], key)); } function vuexInit() { const options = this.$options if (options.store) { // 组件内部设定了store,则优先使用组件内部的store this.$store = typeof options.store === 'function' ? options.store() : options.store } else if (options.parent && options.parent.$store) { // 组件内部没有设定store,则从根App.vue下继承$store方法 this.$store = options.parent.$store } } [代码] 界面代码如下: [代码]export default { name: 'app', created() { console.log('打印出this.$store.getters.getStatePlusOne的结果',this.$store.getters.getStatePlusOne); }, mounted() { setTimeout(() => { this.$store.commit('incrementFive'); console.log('store state自增5后的结果', this.$store.state.count); }, 2000); setTimeout(() => { this.$store.dispatch('countPlusSix'); console.log('store dispatch自增6后的结果', this.$store.state.count); }, 3000); }, computed: { count() { return this.$store.state.count; } } } [代码] 运行结果: 成功在3秒之后dipatch自增6输出11 <h4>五. module方法的实现</h4> main.js代码如下: [代码]const pageA = { state: { count: 100 }, mutations: { incrementA(state) { state.count++; } }, actions: { incrementAAction(context) { context.commit('incrementA'); } } } let store = new Vuex.Store({ modules: { a: pageA }, state: { count: 0 }, actions: { countPlusSix(context) { context.commit('plusSix'); } }, mutations: { incrementFive(state) { // console.log('初始state', JSON.stringify(state)); state.count = state.count + 5; }, plusSix(state) { state.count = state.count + 6; } }, getters: { getStatePlusOne(state) { return state.count + 1 } } }, Vue); [代码] store.js的代码如下: [代码]let _Vue; export class Store { constructor(options = {}, Vue) { _Vue = Vue Vue.mixin({ beforeCreate: vuexInit }) this.getters = {}; this._mutations = {}; // 在私有属性前加_ this._wrappedGetters = {}; this._actions = {}; this._modules = new ModuleCollection(options) const { dispatch, commit } = this; this.commit = (type) => { return commit.call(this, type); } this.dispatch = (type) => { return dispatch.call(this, type); } const state = options.state; const path = []; // 初始路径给根路径为空 installModule(this, state, path, this._modules.root); this._vm = new Vue({ data: { state: state } }); } get state() { // return this.options.state; // 无法完成页面中的双向绑定,所以改用this._vm的形式 return this._vm._data.state; } commit(type) { this._mutations[type].forEach(handler => handler()); } dispatch(type) { return this._actions[type][0](); } } class ModuleCollection { constructor(rawRootModule) { this.register([], rawRootModule) } register(path, rawModule) { const newModule = { _children: {}, _rawModule: rawModule, state: rawModule.state } if (path.length === 0) { this.root = newModule; } else { const parent = path.slice(0, -1).reduce((module, key) => { return module._children(key); }, this.root); parent._children[path[path.length - 1]] = newModule; } if (rawModule.modules) { forEachValue(rawModule.modules, (rawChildModule, key) => { this.register(path.concat(key), rawChildModule); }) } } } function installModule(store, rootState, path, module) { if (path.length > 0) { const parentState = rootState; const moduleName = path[path.length - 1]; _Vue.set(parentState, moduleName, module.state) } const context = { dispatch: store.dispatch, commit: store.commit, } const local = Object.defineProperties(context, { getters: { get: () => store.getters }, state: { get: () => { let state = store.state; return path.length ? path.reduce((state, key) => state[key], state) : state } } }) if (module._rawModule.actions) { forEachValue(module._rawModule.actions, (actionFn, actionName) => { registerAction(store, actionName, actionFn, local); }); } if (module._rawModule.getters) { forEachValue(module._rawModule.getters, (getterFn, getterName) => { registerGetter(store, getterName, getterFn, local); }); } if (module._rawModule.mutations) { forEachValue(module._rawModule.mutations, (mutationFn, mutationName) => { registerMutation(store, mutationName, mutationFn, local) }); } forEachValue(module._children, (child, key) => { installModule(store, rootState, path.concat(key), child) }) } function registerMutation(store, mutationName, mutationFn, local) { const entry = store._mutations[mutationName] || (store._mutations[mutationName] = []); entry.push(() => { mutationFn.call(store, local.state); }); } function registerAction(store, actionName, actionFn, local) { const entry = store._actions[actionName] || (store._actions[actionName] = []) entry.push(() => { return actionFn.call(store, { commit: local.commit, state: local.state, }) }); } function registerGetter(store, getterName, getterFn, local) { Object.defineProperty(store.getters, getterName, { get: () => { return getterFn( local.state, local.getters, store.state ) } }) } // 将对象中的每一个值放入到传入的函数中作为参数执行 function forEachValue(obj, fn) { Object.keys(obj).forEach(key => fn(obj[key], key)); } function vuexInit() { const options = this.$options if (options.store) { // 组件内部设定了store,则优先使用组件内部的store this.$store = typeof options.store === 'function' ? options.store() : options.store } else if (options.parent && options.parent.$store) { // 组件内部没有设定store,则从根App.vue下继承$store方法 this.$store = options.parent.$store } } [代码] 主界面代码如下: [代码]<template> <div id="app"> ==============主页================<br> 主页数量count为: {{count}}<br> pageA数量count为: {{countA}}<br> ==========以下为PageA内容==========<br> <page-a></page-a> </div> </template> <script> import pageA from './pageA'; export default { name: 'app', components: { pageA }, created() { console.log('打印出this.$store.getters.getStatePlusOne的结果',this.$store.getters.getStatePlusOne); }, mounted() { setTimeout(() => { this.$store.commit('incrementFive'); console.log('store state自增5后的结果', this.$store.state.count); }, 2000); setTimeout(() => { this.$store.dispatch('countPlusSix'); console.log('store dispatch自增6后的结果', this.$store.state.count); }, 3000); }, computed: { count() { return this.$store.state.count; }, countA() { return this.$store.state.a.count; } } } </script> [代码] pageA页面如下: [代码]<template> <div> 页面A被加载 </div> </template> <script> export default { name: 'pageA', mounted() { setTimeout(() => { this.$store.dispatch('incrementAAction'); }, 5000) }, } </script> [代码] 运行结果: 在5秒后A页面触发incrementAAction,主界面中的countA变化为101,成功 <span style=“color: red”>自此:基本用了150行左右的代码实现了vuex 80%左右的功能了,其中还有namespace等不能够使用,其他基本都和源代码语法相同,如果你有兴趣仔细再看看,可以移步github仓库代码,代码是建立在阅读了vuex源代码之后写的,所以看完了本文的代码,再去看vuex的代码,相信你一定会一目了然</span> <h4>六. 实现:Vue.use(Vuex)</h4> 最后为了和vuex源代码做到最相似,同样使用Vue.use(Vuex),使用如下的代码进行实现: [代码]export function install(_Vue) { Vue = _Vue; Vue.mixin({ beforeCreate: function vuexInit() { const options = this.$options; if (options.store) { this.$store = options.store; } else if (options.parent && options.parent.$store) { this.$store = options.parent.$store; } } }) } [代码] 部门正在招新,为腾讯企业产品部,隶属CSIG事业群。福利不少,薪水很高,就等你来。有兴趣请猛戳下方两个链接。 https://www.lagou.com/jobs/5210396.html https://www.zhipin.com/job_detail/2876d4cc2cdebe2c1XNz2NW0ElU~.html 参考资料: Build a Vuex Module How does a minimal Vuex implementation look like? 从0开始写一个自己的Vuex vuex 源码:如何实现一个简单的 vuex Vue 源码(三) —— Vuex 浅谈Vue.use Vuex官方文档 vuex Github仓库
2019-03-27 - Vue 移动端框架
1. vonic vonic 一个基于 vue.js 和 ionic 样式的 UI 框架,用于快速构建移动端单页应用,很简约。 中文文档 | github地址 | 在线预览 [图片] 2. vux vux 基于WeUI和Vue(2.x)开发的移动端UI组件库。基于webpack+vue-loader+vux可以快速开发移动端页面,配合vux-loader方便你在WeUI的基础上定制需要的样式。小编在开发微信公众号的时候使用过,欢迎来评论区吐槽。 中文文档 | github地址 | 在线预览 [图片] 3. Mint UI Mint UI 由饿了么前端团队推出的 Mint UI 是一个基于 Vue.js 的移动端组件库。 中文文档 | github地址 | 在线预览 [图片] 4. Muse-UI 基于 Vue 2.0 和 Material Design 的 UI 组件库 中文文档 | github地址 [图片] 5. Vant 是有赞前端团队基于有赞统一的规范实现的 Vue 组件库,提供了一整套 UI 基础组件和业务组件。 中文文档 | github地址 | 在线预览 [图片] 6. Cube-UI 滴滴 WebApp 团队 实现的 基于 Vue.js 实现的精致移动端组件库 中文文档 | github地址 | 在线预览 [图片] 7. vue-ydui Vue-ydui 是 YDUI Touch 的一个Vue2.x实现版本,专为移动端打造,在追求完美视觉体验的同时也保证了其性能高效。目前由个人维护。 中文文档 | github地址 | 在线预览 [图片] 8. Mand-Mobile 面向金融场景的Vue移动端UI组件库,丰富、灵活、实用,快速搭建优质的金融类产品。 中文文档 | github地址 | 在线预览 [图片] 9. v-charts 在使用 echarts 生成图表时,经常需要做繁琐的数据类型转化、修改复杂的配置项,v-charts 的出现正是为了解决这个痛点。基于 Vue2.0 和 echarts 封装的 v-charts 图表组件,只需要统一提供一种对前后端都友好的数据格式设置简单的配置项,便可轻松生成常见的图表。特别感谢 @书简_yu 的贡献。 中文文档 | github地址 | 在线预览 [图片] 10. Vue Carbon Vue Carbon 是基于 vue 开发的material design ui 库。 中文文档 | github地址 | 在线预览 [图片] 11. Quasar Quasar(发音为/kweɪ.zɑɹ/)是MIT许可的开源框架(基于Vue),允许开发人员编写一次代码,然后使用相同的代码库同时部署为网站、PWA、Mobile App和Electron App。使用最先进的CLI设计应用程序,并提供精心编写,速度非常快的Quasar Web组件。 中文文档 | github地址 [图片] 12. Vue-recyclerview 使用vue-recyclerview掌握大型列表。 github地址 | 在线预览 [图片] 13. Vue.js modal 易于使用,高度可定制,移动友好的Vue.js 2.0+ modal。 在线文档 | github地址 | 在线预览 [图片] 14. Vue Baidu Map Vue Baidu Map是基于Vue 2.x的百度地图组件。 中文文档 | github地址 | 在线预览 [图片] 15. Onsen UI 将Vue.js的强大功能和简单性带入混合和渐进式Web应用程序。 在线文档 | github地址 | 在线预览 [图片] 相关文章 Vue PC端框架 别走,还有后续呐······ 如果小伙伴们有比较好的移动端框架,欢迎在评论区留言砸场,我会持续更新在简书上,谢谢你的贡献。
2019-03-26 - 云开发,实现【最近搜索】和【大家在搜】的存储和读取逻辑
小程序【搜索】功能是很常见的,在开发公司的一个电商小程序的时候想尝试使用“云开发”做系统的后端,而首页顶端就有个搜索框,所以第一步想先解决【搜索】的一些前期工作:【最近搜索】和【大家在搜】关键词的存储和读取逻辑。 效果图如下: [图片] 效果视频请点击链接查看: 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