安徽省高等级公路工程建设指挥部网站,高中毕业学网站开发,网站开发整套资料,wordpress文件权限一、缓存机制
1、为什么要加缓存#xff1f;
场景一#xff1a;【等待】#xff0c;在向服务器请求新的数据时。我们让用户看到什么#xff1f;第一种是漂亮的等待加载页面#xff1b;第二种是缓存的内容。对于第二种#xff0c;用户可以对页面进行操作#xff0c;等待…
一、缓存机制
1、为什么要加缓存
场景一【等待】在向服务器请求新的数据时。我们让用户看到什么第一种是漂亮的等待加载页面第二种是缓存的内容。对于第二种用户可以对页面进行操作等待新数据时可以查看旧数据更具有“可操作性”与“可用性”从而减轻了从服务器获取数据这一动作的大小和时间长短增强了用户体验。另一方面如果内容更新的间隔较长或者用户刷新的间隔较短在没有缓存的情况下很多数据我们会多次重复的向服务器获取增加了成本。
场景二【结果】没有联网或者在地铁上网络太差无法加载数据时如果留给用户一个空白页面实在是感觉有点不负责任啊。并且很多功能在没有联网的情况下也有使用的可能性比如APP中的通讯录查看一些聊天记录通知信息文章列表等。因为用户打开APP不一定是要看新信息说不定是回顾老信息或许老信息里也有用户之前没看的所以恰当的缓存可以满足更多的用户场景。
场景三【金钱】有一天一个用户发现自己装了某个APP后流量用的特别快Ta可能永远将这个APP打入冷宫了而增加缓存正是节省流量的一个方法。虽然节省的不多或者用户也察觉不到但是作为一个有态度的产品经理应该多做一些思考。
2、什么是缓存
缓存可分为如下几类
1app缓存。
2固定缓存。
3可手动清理的缓存。
4不可手动清理的缓存。
5临时缓存。
其中临时缓存常用于一个功能页面内保存各栏目的缓存。同一个功能里会把子功能分为多个栏目进行划分每个标签栏目下的内容在本次使用中都可保存为临时缓存在该功能里切换栏目不需要重新加载数据使用缓存显示。
对于用户来说使用时达到了无缝切换浏览对于服务器来说在短时间内数据很少会有更新所以在一般情况下能满足用户的正常需求并达到体验优秀。
临时缓存的清理机制是退出该功能模块就清除之前的缓存。也就是说下次进入该功能模块需要重新获取一次数据。
很多时候我们都会用到临时缓存因为那些信息真的不是那么重要而且不需要经常反复查看那对于那些我们经常使用而且经常需要反复查看的信息马海祥建议采取固定缓存保存在本地方便下次翻阅时不需要再一次向服务器请求数据了。
对于固定缓存又会细分为可手动清理的缓存和不可手动清理的缓存。
第一种是我们最常见的缓存几乎所有产品都采用这种缓存方式。平时用户浏览文章、图集加载的数据就以这种形式缓存在本地下次看回这篇文章、图集时就不需要加载了。用户也可以手动把这些缓存清理了释放空间。
而对于某些特殊场景例如一些相对固定的数据我们不愿意一开始就打包进App里这样会占太大容量造成产品包很大也不愿意每次进入页面都向服务器加载这些信息那怎么办建议的解决方法就是我们可以只加载一次就永远存在本地了这样安装包也不会大以后也不用加载了。
3、如何清理缓存
一般App都会在“设置”里提供一个清理缓存的功能一键把空间释放。除此之外App最好要设计自动清理机制可以通过两个维度来设计这个机制。
1、时间
通过设定一个固定的时间或者根据用户使用周期灵活设定时间来清理缓存。每个产品的场景不一用户使用频率不一设定这个机制的时候就需要结合实际情况考虑了。
2、容量
一般是设定一个容量上限采用堆栈的设计原理进行缓存清理溢出堆栈的旧数据将自动清除。
4、实现 uni.setStorage(OBJECT) 将数据存储在本地缓存中指定的 key 中会覆盖掉原来该 key 对应的内容这是一个异步接口。
uni.setStorage({key: storage_key, //key String 本地缓存中的指定keydata: hello, //data Any 需要存储的内容只支持原生类型、及能够通过 JSON.stringify 序列化的对象success: function () { //success Function 接口调用成功的回调函数console.log(success);}
});uni.setStorageSync(KEY,DATA) 将 data 存储在本地缓存中指定的 key 中会覆盖掉原来该 key 对应的内容这是一个同步接口。
try {uni.setStorageSync(storage_key, hello); //key String 本地缓存中的指定的 key
} catch (e) { //data Any 需要存储的内容只支持原生类型、及能够通过 JSON.stringify 序列化的对象// error
}uni.getStorage(OBJECT) 从本地缓存中异步获取指定 key 对应的内容。
uni.getStorage({key: storage_key, //String 本地缓存中的指定的 keysuccess: function (res) { //Function 接口调用的回调函数res {data: key对应的内容}console.log(res.data); //data key 对应的内容}
});uni.getStorageSync(KEY) 从本地缓存中同步获取指定 key 对应的内容。
try {const value uni.getStorageSync(storage_key); //key String 本地缓存中的指定的 keyif (value) {console.log(value);}
} catch (e) {// error
}uni.getStorageInfo(OBJECT) 异步获取当前 storage 的相关信息。
uni.getStorageInfo({success: function (res) { //Function 接口调用的回调函数.console.log(res.keys); //keys ArrayString 当前 storage 中所有的 keyconsole.log(res.currentSize); //currentSize Number 当前占用的空间大小, 单位kbconsole.log(res.limitSize); //limitSize Number 限制的空间大小, 单位kb}
});uni.getStorageInfoSync() 同步获取当前 storage 的相关信息。
try {const res uni.getStorageInfoSync();console.log(res.keys);console.log(res.currentSize);console.log(res.limitSize);
} catch (e) {// error
}uni.removeStorage(OBJECT) 从本地缓存中异步移除指定 key。
uni.removeStorage({key: storage_key, //key String 本地缓存中的指定的 keysuccess: function (res) { //success Function 接口调用的回调函数console.log(success);}
});uni.removeStorageSync(KEY) 从本地缓存中同步移除指定 key。
try {uni.removeStorageSync(storage_key); //key String 本地缓存中的指定的 key
} catch (e) {// error
}uni.clearStorage() 异步清理本地数据缓存。
uni.clearStorage();uni.clearStorageSync() 同步清理本地数据缓存。
try {uni.clearStorageSync();
} catch (e) {// error
}注意 uni-app的Storage在不同端的实现不同
H5端为localStorage浏览器限制5M大小是缓存概念可能会被清理App端为原生的plus.storage无大小限制不是缓存是持久化的各个小程序端为其自带的storage api数据存储生命周期跟小程序本身一致即除用户主动删除或超过一定时间被自动清理否则数据都一直可用。微信小程序单个 key 允许存储的最大数据长度为 1MB所有数据存储上限为 10MB。支付宝小程序单条数据转换成字符串后字符串长度最大200*1024。同一个支付宝用户同一个小程序缓存总上限为10MB。百度、字节跳动小程序文档未说明大小限制
二、加载机制
1、页面加载
方案1单页面整体加载
这种加载比较简单一般运用在页面内容比较单一的情况下所以直接一次性加载完所有数据后再显示内容。其单页面加载失败的状态相对来说也比较好处理。
方案2单页面分块加载
这种方案的特点是能让用户逐步看到内容在这个渐进的过程中降低用户的焦虑心理。
其中又可以分为模块间有关联性的先加载父内容再加载子内容。如优酷先把栏目加载出来再加载各栏目的内容。
模块间没有绝对关联性的可独自加载各自模块内容根据请求的速度不同分别显示。这样处理有一定几率让用户在没完全刷出数据的情况下就能找到自己需要的功能如大众点评、淘宝客户端等。
框架固定内容更新的可先把框架显示出来再把各模块的数据各自加载显示如各种iOS自带应用。
这种分模块加载的需要特别注意加载失败的状态毕竟每个模块都提示加载失败点击重试是很挫的一件事可以根据信息的优先级来决定哪些数据失败了采用默认状态哪些数据采用失败提示。
方案3跨页面加载
父页面子页面 or 同一app内页面间字段可以复用的在加载子页面时不需要重新加载新数据。
方案4预加载
这种加载方式的特点是在加载一个页面内容的同时预测用户的下一步行为并为他下一步需要使用的页面加载内容使得他在下一步的操作中能立刻获取信息而不需要加载等待。
预加载提供给用户无缝的产品使用体验使得用户在使用产品的过程中更直接流畅没有被打断的感觉。
具体的例子有
在浏览图集的时候当看到第一张的图片时就自动后台加载第二第三第四张图片用户浏览完第一张图片切换到第二张时就不会有加载等待的过程。
在浏览新闻列表时就把每篇新闻的内容在后台进行预加载用户选择看某篇新闻时能立刻阅读到内容。
但是这种方案也需要面临很多的问题马海祥觉得最直接的就是流量问题因为会自动跑掉很多用户可能根本用不上的数据流量所以一般情况下马海祥建议可以设定在wifi环境才采用这种加载模式。又或者设定加载规则只把主要内容预加载而部分次要内容可以在用户真的用到的时候才加载例如预加载新闻正文的情况可以只加载文本信息图片信息等到用户进入内页才加载。这种预加载与分块加载结合的方式也普遍运用在各个场景。
另外预加载也需要时间的他只是不在客户端显示给用户默默在后台运作而已需要特殊考虑未加载完用户就使用到那些信息的情况所以在做预加载设计时需要同时考虑另一种适合该情况的普通加载方式。
预加载需要根据具体的场景来进行设计设定好信息优先级综合考虑各种类型信息的具体大小流量整体考虑预加载的方式这些都是需要经过精心分析思考的。
随着网络环境的发展预加载将成为以后产品普遍的加载方式他提供给用户的无缝使用体验大大地提升了产品的可用性。
2、操作加载
除了页面的信息需要加载页面内的操作也是需要通过给服务器发送请求记录的。
方案1加载层
进行一个操作后弹出模态的提示层告知用户正在加载。采用模态的提示主要是防止用户在该过程中进行其他操作导致当前加载出错。由于采用模态的提示并且有可能因为网络原因导致长时间处于加载状态建议提供一个“关闭”的操作中止本次加载恢复App可用状态。加载失败时可在当前浮层变换为失败提示。模态提示层是最稳妥的方式但他会使用户在使用过程中有打断的感觉。
方案2控件自身加载状态
这种方式是把操作加载的状态与控件的样式结合起来了对某个控件进行操作后控件变换为加载状态此时控件不能重复操作。由于这种加载方式是控件的自身状态不影响其他操作所以用户也可以对页面进行其他操作可能会导致同时有多个请求的情况增加了加载失败的风险这也算是这种方式的弊端不过这种极端情况很少出现。请求失败后可配合Toast提示告知用户失败的原因。
方案3后台加载
用户在操作后客户端立刻反馈操作成功然后把请求放到后台与服务器交互这一过程用户不需要了解不需要等待在正常情况下体验是非常棒的。
但是在极端情况下会出现一些莫名其妙的状况由于是后台记录请求并与服务器交互所以实际请求是否成功客户端是不说明的全部以操作成功来显示这就会导致用户误以为操作成功了但实际上下次来看发现没有成功。
所以这种加载方式是需要根据具体使用场景来权衡使用的对于一些重要的操作建议还是使用模态的方式加载对于一些小操作如点赞、订阅、关注可采用后台加载的方式。
3、下一页加载还是当前也加载
用户进入首页正式迈出体验的第一步接下来迎接的就是基于用户目标的界面间跳转。完成界面的跳转会有各种加载策略但无论形式如何我们都可以将其归为两大类“下一页加载”、“当前页加载”。
1“下一页加载”满足了用户提前窥视的需求
我们把页面看成“点”页面流是连接这些点的“线”我们以“用户想买一条牛仔裤”这一场景作为案例做了简单的眼动研究从应用启动到商品浏览再到商品确定最后进入下单页用户所呈现的瞳孔梯次增大即EDCBA为了解释这一现象通过与被试交流我们发现相比于各种浏览用户更期待看到他们想看到的东西。因此此时的”下一页加载“正好满足了用户提前窥视的需求。
2 WaitI Need Think Think
我们以同样的方式又对“使用支付宝对手机充值”这一场景做了研究从开始支付到二次确认支付用户所呈现的瞳孔都比较大即A与B近似相等通过访谈我们发现与“递增体验流”不同的是当用户遇到判断逻辑的界面时用户并非急于想看下一页面到底包含怎样的内容而是非0即1的验证心态即我的操作效成功了吗因此在判断逻辑界面中用户的内容窥视需求并不强当然也没什么内容要么仅是一个小小的Toast再大一点就是一个简单的信息反馈界面意味着“下一页加载”在这里就是个鸡肋用户反而对非0即1的验证需求较为强烈其中还伴随着等待结果过程中的紧张感、激动感因此界面通过当前页加载表明系统正在努力地处理用户交代的指令迎合了用户的紧张感、激动感直到结果显现——“处理成功”完成了非0即1验证的满足感。
4、先加载还是先展示
当需加载的是功能时可以先展示再加载当需加载的是内容时则反过来。
淘宝
打开APP的第一个页面是功能所以先展示再加载的
随便点击一个模块不要点菜单下面要展示的将要是内容商品所以是先加载再展示的没有加载完都不展示
京东
同样的功能模块先展示后加载
内容先加载没加载完不展示
两种方式各有利弊
先展示后加载
优点给用户0等待的错觉
缺点当前数据有可能是错的而且得等用户操作到最后一步才会发现
先加载后展示
优点保证数据的质量和准确
缺点网络不好时造成等待
显然功能模块对于一个产品来说是既有固定的在短时间内几乎不会更新所以这种数据出现错误或与当前状态不同的几率小得多因此可以使用先展示后加载的方式。
另一方面内容特别是商品数据是最容易产生变动的为了保证每一个消费者看到的数据都是最真实最准确的所以务必要先加载再展示。
5、实现 //加载更多的新闻getMoreNews(){this.loadingTxt加载中uni.showNavigationBarLoading()uni.request({url:https://demo.hcoder.net/index.php?userhcoderpwdhcodermList1pagepage,success: (res) {if(res.datanull){this.loadingTxt已经加载全部return}this.newListthis.newList.concat(res.data.split(--hcSplitor--))// this.newList[...this.newList,res.data.split(--hcSplitor--)]//停止下拉样式uni.stopPullDownRefresh()//隐藏标题读取uni.hideNavigationBarLoading()page}})},三、刷新机制
1、空白页面刷新失败有提示
现在的应用都标榜以内容为中心所以都会极力避免空白页面的出现。对于大部分的应用最好的方法就是使用缓存进入页面之后先显示之前的缓存然后再进行内容的刷新。其次消灭空白页面的第二种方法就是提供系统推荐项进行替代。但是对于一些页面页面内容跟用户的使用状态关系密切无法避免会出现空白页面这时候会使用一些引导类的提示使得页面变得更加丰富同时可以促进用户产生内容。
但是一些资讯类应用比如读读日报打开默认是空白页面然后再加载内容我不是很明白这种设定。其他一些应用比如豆瓣一刻和MONO每天第一次进入应用的时候也会出现空白页面。我猜想第二类应用的展示方式的原因是这样的。他们的内容推送都是严格以天为单位的每天固定时段推送精选内容。他们会希望你每天只看并且看完当天的东西所以一旦到了第二天昨天的内容就是累赘了。所以每天第一次进入应用的时候会出现空白页面象征着每天都是从新开始。此时就会对应一个“空白刷新”逻辑。
空白刷新对应的场景是这样子的用户想要刷新出内容并且用户知道这里可以刷出新内容但是没有刷新成功这时候需要给用户一个交待。所以需要提示用户。同时提示完用户之后需要给用户一个解决方法这就是“点击后重试”。
2、缓存页面刷新失败无提示
常见的应用比如知乎、网易新闻、好奇心日报、微信朋友圈等这些应用都会采用缓存的形式打开之后显示的是缓存内容然后系统会给服务器发送请求如果有内容更新的话就会自动更新一次内容更新之后的内容直接覆盖当前的内容。更新失败之后是没有提示的。但是有一些应用比如有道词典、企鹅FM、网易云音乐等他们更新失败之后是有提示的。
我觉得这两种应用的区分点在于
应用的使用频率
内容的时间连续性
界面之间的关系紧密度。
比如说网易新闻作为一个打发时间的工具每天使用频率就会比较高所以用户进来之后是想看看有没有更新。其次网易新闻的内容是连续不断更新的所以用户会知道当前显示的内容是我看看过并且处理过的。最后新闻列表页面显示的是摘要用户可以通过摘要快速进行判断是否要进入详情页摘要有助于帮助用户回忆上一次的使用场景。
所以这就对应着一个这样的场景用户只是想看看有没有更新所以他们已经做好了“没有新内容”的心理预期所以即使是更新不了内容用户也不会想太多。反倒是如果进行了错误提示用户可能会有一种挫败感。因为他知道现在有内容只是因为网络的原因而没有更新他要进行的任务受到了外界因素的阻碍由此产生一种细微的挫败感。
3、缓存页面刷新失败有提示
另一类应用使用频率没那么高或者内容不具备时间连续性的又或者说当前界面无法唤起用户上一次的使用场景。那么就有必要进行率先你失败提示了。
比如说企鹅FM音频类的应用注定使用不会那么频繁因为通过视觉接收的信息会比通过听觉接收的信息更快更多同时音频类对环境的要求较高比如用耳机时要求环境不那么嘈杂外放时要求在私人场所。其次此类应用都是实时推荐的不存在时间连续性的问题用户无法通过时间来判断内容是否被阅读过。再者标题也无法帮你快速做出判断你还是要进去听过才知道内容是什么。最后如果不提醒用户进入到详情页再收到提醒就会觉得应用浪费了用户的时间。所以对于此类内容刷新失败是有必要进行提醒的。
4、实现 onPullDownRefresh 在 js 中定义 onPullDownRefresh 处理函数和onLoad等生命周期函数同级监听该页面用户下拉刷新事件。
需要在 pages.json 里找到的当前页面的pages节点并在 style 选项中开启enablePullDownRefresh。当处理完数据刷新后uni.stopPullDownRefresh 可以停止当前页面的下拉刷新。uni.startPullDownRefresh(OBJECT)
{pages: [{path: pages/index/index,style: {navigationBarTitleText: uni-app,enablePullDownRefresh: true}}],globalStyle: {navigationBarTextStyle: white,navigationBarBackgroundColor: #0faeff,backgroundColor: #fbf9fe}
}uni.startPullDownRefresh(OBJECT) 开始下拉刷新调用后触发下拉刷新动画效果与用户手动下拉刷新一致。
export default {data: {text: uni-app},onLoad: function (options) {setTimeout(function () {console.log(start pulldown);}, 1000);//uni.startPullDownRefresh();uni.startPullDownRefresh({successfunction(res){console.log(res); //success 返回参数说明}}); //这里表示当进入页面的时候就开始执行下拉刷新动画},onPullDownRefresh() {//监听下拉刷新动作的执行方法每次手动下拉刷新都会执行一次console.log(refresh);setTimeout(function () {uni.stopPullDownRefresh(); //停止下拉刷新动画}, 1000);}
}uni.stopPullDownRefresh() 停止当前页面下拉刷新。
注意
支付宝小程序startPullDownRefresh在开发者工具里会提示暂未开放请勿使用支付宝小程序startPullDownRefresh请使用真机调试非真机预览后续支付宝小程序开发工具更新可能会有所修改
上拉加载、下拉刷新
{path: pages/index/index,style: {navigationBarTitleText: uni-app,enablePullDownRefresh:true}}index.vue
templateviewview v-for(item,index) of newList :keyindex classnewList{{item}}/viewview classloading{{loadingTxt}}/view/view
/templatescript
let page1,timernullexport default {data() {return {newList:[],loadingTxt:加载更多}},onLoad(e) {this.getNews()},onPullDownRefresh() {//下拉的生命周期this.getNews()},onReachBottom() {//阻止重复加载if(timer ! null){clearTimeout(timer)}timersetTimeout(()this.getMoreNews(),500)// this.getMoreNews()},methods: {//下拉刷新事件getNews(){page1//标题读取样式激活uni.showNavigationBarLoading()uni.request({url:https://demo.hcoder.net/index.php?userhcoderpwdhcodermList1page1,success: (res) {this.newListres.data.split(--hcSplitor--)//停止下拉样式uni.stopPullDownRefresh()//隐藏标题读取uni.hideNavigationBarLoading()page}})},//加载更多的新闻getMoreNews(){this.loadingTxt加载中uni.showNavigationBarLoading()uni.request({url:https://demo.hcoder.net/index.php?userhcoderpwdhcodermList1pagepage,success: (res) {if(res.datanull){this.loadingTxt已经加载全部return}this.newListthis.newList.concat(res.data.split(--hcSplitor--))// this.newList[...this.newList,res.data.split(--hcSplitor--)]//停止下拉样式uni.stopPullDownRefresh()//隐藏标题读取uni.hideNavigationBarLoading()page}})},}}
/scriptstyle.newList{line-height: 2em;padding: 20px;}.loading{line-height: 2em;text-align: center;color: #888;margin-top: 30rpx;}
/style