0

    Uniapp (app端) 缓存、加载、刷新机制

    2023.07.21 | admin | 154次围观

    一、缓存机制

    1、为什么要加缓存?

    场景一:【等待】,在向服务器请求新的数据时。我们让用户看到什么?第一种是漂亮的等待加载页面;第二种是缓存的内容。对于第二种,用户可以对页面进行操作,等待新数据时可以查看旧数据,更具有“可操作性”与“可用性”,从而减轻了从服务器获取数据这一动作的大小和时间长短,增强了用户体验。另一方面,如果内容更新的间隔较长或者用户刷新的间隔较短,在没有缓存的情况下,很多数据我们会多次重复的向服务器获取,增加了成本。

    场景二:【结果】没有联网,或者在地铁上网络太差无法加载数据时,如果留给用户一个空白页面,实在是感觉有点不负责任啊。并且很多功能在没有联网的情况下也有使用的可能性,比如:APP中的通讯录,查看一些聊天记录,通知信息,文章列表等。因为用户打开APP不一定是要看新信息,说不定是回顾老信息(或许老信息里也有用户之前没看的),所以恰当的缓存可以满足更多的用户场景。

    场景三:【金钱】有一天,一个用户发现自己装了某个APP后流量用的特别快,Ta可能永远将这个APP打入冷宫了,而增加缓存正是节省流量的一个方法。虽然节省的不多或者用户也察觉不到,但是作为一个有态度的产品经理,应该多做一些思考。

    2、什么是缓存?

    缓存可分为如下几类:

    (1)app缓存。

    (2)固定缓存。

    (3)可手动清理的缓存。

    (4)不可手动清理的缓存。

    (5)临时缓存。

    其中,临时缓存常用于一个功能页面内,保存各栏目的缓存。同一个功能里会把子功能分为多个栏目进行划分,每个标签栏目下的内容在本次使用中都可保存为临时缓存,在该功能里切换栏目,不需要重新加载数据,使用缓存显示。

    对于用户来说,使用时达到了无缝切换浏览,对于服务器来说,在短时间内数据很少会有更新,所以在一般情况下能满足用户的正常需求,并达到体验优秀。

    临时缓存的清理机制是:退出该功能模块就清除之前的缓存。也就是说下次进入该功能模块,需要重新获取一次数据。

    很多时候我们都会用到临时缓存,因为那些信息真的不是那么重要,而且不需要经常反复查看,那对于那些我们经常使用而且经常需要反复查看的信息,马海祥建议采取固定缓存,保存在本地,方便下次翻阅时不需要再一次向服务器请求数据了。

    对于固定缓存又会细分为可手动清理的缓存和不可手动清理的缓存。

    第一种是我们最常见的缓存,几乎所有产品都采用这种缓存方式。平时用户浏览文章、图集加载的数据就以这种形式缓存在本地,下次看回这篇文章、图集时就不需要加载了。用户也可以手动把这些缓存清理了,释放空间。

    而对于某些特殊场景后台应用刷新什么意思,例如一些相对固定的数据,我们不愿意一开始就打包进App里,这样会占太大容量,造成产品包很大,也不愿意每次进入页面都向服务器加载这些信息,那怎么办?建议的解决方法就是我们可以只加载一次就永远存在本地了,这样安装包也不会大,以后也不用加载了。

    3、如何清理缓存?

    一般App都会在“设置”里提供一个清理缓存的功能,一键把空间释放。除此之外,App最好要设计自动清理机制,可以通过两个维度来设计这个机制。

    (1)、时间

    通过设定一个固定的时间,或者根据用户使用周期灵活设定时间来清理缓存。每个产品的场景不一,用户使用频率不一,设定这个机制的时候就需要结合实际情况考虑了。

    (2)、容量

    一般是设定一个容量上限,采用堆栈的设计原理进行缓存清理,溢出堆栈的旧数据将自动清除。

    4、实现

    uni.setStorage(OBJECT)

    将数据存储在本地缓存中指定的 key 中,会覆盖掉原来该 key 对应的内容,这是一个异步接口。

    uni.setStorage({
        key: 'storage_key',          //key String 本地缓存中的指定key
        data: '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	本地缓存中的指定的 key
        success: 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	本地缓存中的指定的 key
        if (value) {
            console.log(value);
        }
    } catch (e) {
        // error
    }
    

    uni.getStorageInfo(OBJECT)

    异步获取当前 storage 的相关信息。

    uni.getStorageInfo({
        success: function (res) { //Function 接口调用的回调函数.
            console.log(res.keys);  //keys	Array<String>	当前 storage 中所有的 key
            console.log(res.currentSize); //currentSize	Number	当前占用的空间大小, 单位:kb
            console.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 本地缓存中的指定的 key
        success: 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在不同端的实现不同:

    二、加载机制

    1、页面加载

    方案1:单页面整体加载

    这种加载比较简单,一般运用在页面内容比较单一的情况下,所以直接一次性加载完所有数据后再显示内容。其单页面加载失败的状态相对来说也比较好处理。

    方案2:单页面分块加载

    这种方案的特点是,能让用户逐步看到内容,在这个渐进的过程中降低用户的焦虑心理。

    其中又可以分为,模块间有关联性的,先加载父内容,再加载子内容。如优酷,先把栏目加载出来,再加载各栏目的内容。

    模块间没有绝对关联性的,可独自加载各自模块内容,根据请求的速度不同分别显示。这样处理有一定几率让用户在没完全刷出数据的情况下就能找到自己需要的功能,如大众点评、淘宝客户端等。

    框架固定,内容更新的,可先把框架显示出来,再把各模块的数据各自加载显示,如各种iOS自带应用。

    这种分模块加载的需要特别注意加载失败的状态,毕竟每个模块都提示加载失败,点击重试是很挫的一件事,可以根据信息的优先级来决定哪些数据失败了采用默认状态,哪些数据采用失败提示。

    方案3:跨页面加载

    父页面&子页面 or 同一app内,页面间字段可以复用的,在加载子页面时不需要重新加载新数据。

    方案4:预加载

    这种加载方式的特点是,在加载一个页面内容的同时,预测用户的下一步行为,并为他下一步需要使用的页面加载内容,使得他在下一步的操作中能立刻获取信息而不需要加载等待。

    预加载提供给用户无缝的产品使用体验,使得用户在使用产品的过程中更直接流畅,没有被打断的感觉。

    具体的例子有:

    在浏览图集的时候,当看到第一张的图片时,就自动后台加载第二第三第四张图片,用户浏览完第一张图片切换到第二张时就不会有加载等待的过程。

    在浏览新闻列表时,就把每篇新闻的内容在后台进行预加载,用户选择看某篇新闻时,能立刻阅读到内容。

    但是这种方案也需要面临很多的问题,马海祥觉得最直接的就是流量问题,因为会自动跑掉很多用户可能根本用不上的数据流量,所以后台应用刷新什么意思,一般情况下马海祥建议可以设定在wifi环境才采用这种加载模式。又或者设定加载规则,只把主要内容预加载,而部分次要内容可以在用户真的用到的时候才加载,例如预加载新闻正文的情况,可以只加载文本信息,图片信息等到用户进入内页才加载。这种预加载与分块加载结合的方式也普遍运用在各个场景。

    另外,预加载也需要时间的,他只是不在客户端显示给用户,默默在后台运作而已,需要特殊考虑未加载完用户就使用到那些信息的情况,所以在做预加载设计时需要同时考虑另一种适合该情况的普通加载方式。

    预加载需要根据具体的场景来进行设计,设定好信息优先级,综合考虑各种类型信息的具体大小流量,整体考虑预加载的方式,这些都是需要经过精心分析思考的。

    随着网络环境的发展,预加载将成为以后产品普遍的加载方式,他提供给用户的无缝使用体验大大地提升了产品的可用性。

    2、操作加载

    除了页面的信息需要加载,页面内的操作也是需要通过给服务器发送请求记录的。

    方案1:加载层

    进行一个操作后,弹出模态的提示层,告知用户正在加载。采用模态的提示主要是防止用户在该过程中进行其他操作,导致当前加载出错。由于采用模态的提示,并且有可能因为网络原因导致长时间处于加载状态,建议提供一个“关闭”的操作,中止本次加载,恢复App可用状态。加载失败时可在当前浮层变换为失败提示。模态提示层是最稳妥的方式,但他会使用户在使用过程中有打断的感觉。

    方案2:控件自身加载状态

    这种方式是把操作加载的状态与控件的样式结合起来了,对某个控件进行操作后,控件变换为加载状态,此时控件不能重复操作。由于这种加载方式是控件的自身状态,不影响其他操作,所以用户也可以对页面进行其他操作,可能会导致同时有多个请求的情况,增加了加载失败的风险,这也算是这种方式的弊端,不过这种极端情况很少出现。请求失败后,可配合Toast提示告知用户失败的原因。

    方案3:后台加载

    用户在操作后,客户端立刻反馈操作成功,然后把请求放到后台与服务器交互,这一过程用户不需要了解,不需要等待,在正常情况下体验是非常棒的。

    但是在极端情况下会出现一些莫名其妙的状况,由于是后台记录请求并与服务器交互,所以实际请求是否成功客户端是不说明的,全部以操作成功来显示,这就会导致用户误以为操作成功了,但实际上下次来看发现没有成功。

    所以,这种加载方式是需要根据具体使用场景来权衡使用的,对于一些重要的操作,建议还是使用模态的方式加载,对于一些小操作,如点赞、订阅、关注,可采用后台加载的方式。

    版权声明

    本文仅代表作者观点。
    本文系作者授权发表,未经许可,不得转载。

    发表评论