微信小程序wx.setStorage数据缓存实现缓存过期时间
我接到的需求在购物车列表页面，点击关闭降价提醒的时候会把当前显示的文案(mainData.cart.cart_popups.txt)和当前时间缓存下来，下次刷新的时候，拿新的降价文案和缓存的对比，如果不一样，就显示新的。如果一样，就继续对比当前时间和上一次缓存的时间有没有超过delay那个时间，如果没超过就不显示，反之显示。这个时间是换算成时间戳，时间差是25秒(mainData.cart.cart_popups.delay字段)，这个是配置的。cart_popups:{ delay:25, item_keys:["7021052500026_ht141023p2224148t1"], txt:"购物车有1件商品降价了", type:"bottom" }/** * 关闭顶部的降价提示条 */ closeReducePriceTips() { this.setData({ isShowReducePriceTips: false }); // 缓存当前显示的文案和过期时间（ms） const timeStamp = +new Date(); const reducePriceData = { text: this.data.mainData.cart.cart_popups.txt, expires: timeStamp + parseInt(this.data.mainData.cart.cart_popups.delay, 10) * 1000 }; wx.setStorage({ key: 'reducePriceTips', data: reducePriceData }); },微信的数据缓存介绍为了项目性能等方面的考虑，有时候有必要为用户经常访问的页面使用缓存机制；在技术上，服务端和前端都有相应的缓存机制。比如传统的session及cookie等等，在微信小程序中，并没有cookie机制，但有本地缓存。小程序官方文档对本地缓存的介绍如下(传送门)：每个微信小程序都可以有自己的本地缓存，可以通过 wx.setStorage（wx.setStorageSync）、wx.getStorage（wx.getStorageSync）、wx.clearStorage（wx.clearStorageSync）可以对本地缓存进行设置、获取和清理。同一个微信用户，同一个小程序 storage 上限为 10MB。localStorage 以用户维度隔离，同一台设备上，A 用户无法读取到 B 用户的数据。注意： 如果用户储存空间不足，我们会清空最近最久未使用的小程序的本地缓存。我们不建议将关键信息全部存在 localStorage，以防储存空间不足或用户换设备的情况。上面一句翻译成简洁的话就是：本地缓存localStorage存在于客户端中，不同用户所对应的缓存数据是相互独立并且因设备而隔离的，而且是持久存储（除非用户清空该小程序的数据）。实际开发在项目开发过程中，我们利用缓存机制的时候，一般的都需要设置一个过期时间，而不管是session还是cookie我们除了可以设置、获取和清理缓存外，我们可以在设置缓存的同时设置一个“过期时间”。而从以上小程序关于数据缓存API的介绍中，我们并没有看到关于数据缓存过期时间设置的API。存在的问题那么这样似乎对于很多需要使用到缓存机制的场景留下遗憾，以我们的小程序为例，首页的几乎所有的数据全部是通过服务端API提供的数据，如果每次刷新小程序页面都需要重新通过wx.request这个接口拉取数据，那么花在网络请求的时间至少是100ms以上，这不仅不利于用户体验也浪费了计算资源。而如果，直接按照文档中的表面化的应用数据缓存API，那么数据会持久化存储，那么首页中包括案例以及其他动态更新的数据怎么更新？解决办法那么是不是就必须留下这样的遗憾呢？如果果然会留下这样的遗憾，那么我相信腾讯肯定会解决这样的问题的。其实我们可以参考诸如cookie这样的缓存机制，我们自己“造”一个过期时间。在设置某缓存数据A的时候，我们可以同时设置一个过期时间值的数据缓存B；在下一次打开该页面的时候，不仅需要判断数据A是否存在，也需要比较B与当前时间，如果符合要求则使用本地缓存的数据A，否则则重新拉取数据并刷新A和B。以上逻辑翻译成代码则为如下，首先是拉取服务端数据成功后同时设置两个数据缓存，以上的index_data是我们需要设置的缓存数据，而index_data_expiration则是与之对应的时间数据，其中3000000则是3000s，当用户进入该页面时，进行缓存数据判断：下图错误之处，expiration、index_data应是object在项目实践过程中，我们可以根据以上逻辑把这种方法封装起来；此外，缓存的过期时间，我们也可以读取服务端设置的超时时间，这样小程序发布后我们可以在服务端灵活变更调节具体的数值了。文章来源
说明最近涉及到这部分的功能，需要在小程序中打开一个app，然后将小程序uid和app uid进行绑定功能。所以查看了微信的官方文档，梳理了下流程。需求文档[...]
法1 --小程序动画小程序的rotate动画在非<cover-*>标签上使用时没有什么问题的，但是在<cover-*>标签上使用时，我发现ios下只支持-180~180度的旋转（官方文档上也有范围备注），若超过则不旋转（如0~360度）。所以我让设计师出了一个图标旋转了-180度的图使用。 .refresh-btn { position: fixed; bottom: 195rpx; left: 8rpx; width: 102rpx; max-width: 102rpx; min-width: 102rpx; height: 102rpx; max-height: 102rpx; min-height: 102rpx; visibility: visible !important; /* 设置初始位置，因 只支持-180~180度的旋转 */ transform: rotate(-180deg); /*stylelint-disable*/ -ms-transform: rotate(-180deg); -moz-transform: rotate(-180deg); -webkit-transform: rotate(-180deg); -o-transform: rotate(-180deg); }onLoad: function (options) { this.isIosEquipment(); // 刷新按钮的动画init this.refreshBtnAnimation = wx.createAnimation({ transformOrigin: '50% 50% 0', duration: 150, timingFunction: 'linear', delay: 0 }); }, /** * 是否是ios设备 */ isIosEquipment:function() { var that = this; wx.getSystemInfo({ success: function (res) { // ios系统 if (res.system.toLowerCase().indexOf('ios') > -1 || res.platform.toLowerCase().indexOf('ios') > -1) { that.setData({ isIos:true }); } } }); },复位原因：（查看参考文章） this.refreshBtnAnimation.rotate(180).step({duration: 600});这行代码表示，让它在600ms(初始化创建的时间)内旋转到180度，而是此时已处理180度啦，你再点击当然它不会再旋转了。它会不停报怨”我已在180度了呀，你还想怎么样？！...“所以，此时，我们能不能直接再让旋转360度，那么它不就相对于180度后的状态又转了180度了吗？可是看看官网，旋转的范围是-180~180度，既使没有这么范围限制，那么我们也会折腾死，不是吗？每次都要1802，1803...解决办法：就是直接让它复位归0度！这个归0度的动画时间必须要短，不然就要让人看到了一个”倒旋转的过程“，那么0s复位到0度，用户就查觉不到了。具体代码如下：/** * 刷新按钮的旋转动画 * 注： 只支持-180~180度的旋转 */ rotateAnimate: function () { if (!this.data.isIos) { this.refreshBtnAnimation.rotate(180).step({duration: 600}); this.setData({ animationData: this.refreshBtnAnimation.export() }); // 复位至-180度（修复二次点击时不旋转bug,因已到达180度） clearTimeout(this.recoverRotateTimer); this.recoverRotateTimer = setTimeout(function() { this.refreshBtnAnimation.rotate(-180).step({duration: 0}); this.setData({ animationData: this.refreshBtnAnimation.export() }); }.bind(this), 600); } else { // 因ios下不可直接旋转-180至180度，否则会无旋转效果或旋转时位置乱跑偏等。so分为n个阶段 this.refreshBtnAnimation.rotate(-90).step({duration: 150, transformOrigin: '50% 50% 0'}); this.setData({ animationData: this.refreshBtnAnimation.export() }); var angle = -90; var timer = setInterval(function() { if (angle >= 180) { clearInterval(timer); return; } angle += 90; this.refreshBtnAnimation.rotate(angle).step({duration: 150, transformOrigin: '50% 50% 0'}); this.setData({ animationData: this.refreshBtnAnimation.export() }); }.bind(this), 150); // 复位（分多阶段复位） clearTimeout(this.recoverRotateTimer); this.recoverRotateTimer = setTimeout(function() { var angle = 180; var timer = setInterval(function() { if (angle <= -180) { clearInterval(timer); return; } angle -= 90; this.refreshBtnAnimation.rotate(angle).step({duration: 0, transformOrigin: '50% 50% 0'}); this.setData({ animationData: this.refreshBtnAnimation.export() }); }.bind(this), 0); }.bind(this), 600); } }, /** * 刷新订单 */ refreshAction:function() { this.rotateAnimate(); clearTimeout(this.refreshTimer); this.refreshTimer = setTimeout(function() { // 请求API接口 this.getCarrierTrakingInfo(); }.bind(this), 600); }法2 --css法（适合H5，不适合小程序） .refresh-btn { position: fixed; bottom: 195rpx; left: 8rpx; width: 102rpx; height: 102rpx; /* 因 只支持-180~180度的旋转 */ transform: rotate(-180deg); } .rotate-animate { animation: rotateAnimate 600ms ease; -moz-animation: rotateAnimate 600ms ease; -webkit-animation: rotateAnimate 600ms ease; -o-animation: rotateAnimate 600ms ease; } @keyframes rotateAnimate { 0% { transform: rotate(0deg); -ms-transform: rotate(0deg); -moz-transform: rotate(0deg); -webkit-transform: rotate(0deg); -o-transform: rotate(0deg); } 100% { transform: rotate(360deg); -ms-transform: rotate(360deg); -moz-transform: rotate(360deg); -webkit-transform: rotate(360deg); -o-transform: rotate(360deg); } }/** * 刷新订单 */ refreshAction:function() { this.setData({ isActive:true }); clearTimeout(this.refreshTimer); this.refreshTimer = setTimeout(function() { this.getCarrierTrakingInfo(); this.setData({ isActive:false }); }.bind(this), 650); }
微信小程序-分享到朋友圈功能之曲线救国
说明描述：在做换装游戏的结果分享页中，有一个『分享到朋友圈』的按钮和对应功能，但由于微信没有开放小程序直接分享到朋友圈的相关api，所以只能另辟蹊径来达到此目的，具体如下文。注意：将小程序『分享给好友』是图文链接，图是一张固定的图；『分享到朋友圈』就是作品分享图，两者不一样。[...]
说明描述：在此次换装游戏的结果分享页中，由于用户点击『分享到朋友圈』按钮后，将生成一张图片并保存到本地相册中（原因见传送门），而这张图片是需要根据用户所选装扮、得分进行动态绘制的，所以我采用了微信canvas绘图相关的api。现在将此次快速开发中使用canvas踩的坑做个小结，方便自己随时查阅回顾。[...]
wx.authorize(OBJECT) 授权获取相关信息提前向用户发起授权请求。调用后会立刻弹窗询问用户是否同意授权小程序使用某项功能或获取用户的某些数据，但不会实际调用对应接口。如果用户之前已经同意授权，则不会出现弹窗，直接返回成功。注意：scope 为 "scope.userInfo" 时，无法弹出授权窗口，请使用:<tbutton open-type="getUserInfo"> 聚美在guideSetAuth.wxml模板中提供此点击按钮来触发授权弹窗的。如下代码： 开启授权 模板效果：点击'开启授权'后，触发bindgetuserinfo事件，将弹窗询问是否同意授权使用 获取用户信息 的权限。效果图如下：wx.getSetting(OBJECT)获取用户的当前设置。注：返回值中只会出现小程序已经向用户请求过的权限。
作者：蔡依辰说明：对小程序商品分类、首页的轮播图和快捷按钮功能，在添加、拖拽排序时获取sort值，并按sort值从大到小排列。思路分类是按自上而下、按sort值从大到小的顺序排列。分类sort值图：添加分类时获取其sort值添加时，即向队列中push数据，添加项的sort值=分类列表中最大的sort值+SORT_ADD_UNIT_NUM。注意：首次添加时，可以将分类列表中最大的sort值看做虚线框中的0。代码 // SORT_ADD_UNIT_NUM：拖拽排序时sort加的单元值 // 获取sort值(最大sort值+SORT_ADD_UNIT_NUM) addCategoryInfo.sort = $scope.goodCategoryList.length == 0 ? SORT_ADD_UNIT_NUM : $scope.goodCategoryList[0].sort + SORT_ADD_UNIT_NUM;拖拽排序时获取其最新sort值拖拽排序时可能有以下三种情况： 向中间插入：取插入后位置的上下两项的sort值，再求和/2； 向顶部插入：类似添加分类； 向底部插入：取插入后位置的上下两项的sort值，再求和/2（可以将下方项的sort值看做虚线框中的0）。注意当非常多次的连续拖拽时，会造成sort值的位数越来也长，以至于请求api接口时会丢失部分后面的位数值。为了减少发生这个问题的次数，如当拖拽到第10次时就会出现这个问题，我们可以优化到拖拽到第15次或者甚至更多次后才会出现这个问题。如下解决思路。解决思路这个思路在将新版编辑器v2.0中实现，现在还未实现当检测到sort值已经被分到小数点滴三位时，如将一个项拖拽到0.0002 和0.0001之间时，按之前的方式将会得到新sort值0.00015。新方式：在最小的0.0001的基础上，小数点再往后移动一位（即0.00010）且最后一位加2（这个值可以设计下），得到新sort值0.00012。代码 /** * 获取当前分类新的sort值 */ (function getNewSort() { // 往中间插入 if (prevItemId > 0 && nextItemId > 0) { thisItemSort = (prevItemSort + nextItemSort) / 2; } else if (!prevItemId && nextItemId > 0) { // 往顶部插入（类似新加） thisItemSort = nextItemSort + SORT_ADD_UNIT_NUM; } else if (prevItemId && !nextItemId) { // 往底部插入 thisItemSort = (prevItemSort + 0) / 2; } })();拖拽排序示例备注注意：采用angular ng-repeat循环显示的列表，若有通过$index作为下标进行增删改查等操作，则不建议在view视图层上直接使用sort排序（因为sort排序改变的仅仅是视图层显示的顺序），建议先对数据进行排序处理后再渲染到view视图层上，否则很容易出现controller层操作的项非视图层选择的项。