IOS WebView 的返回缓存问题分析

相关工具

  • IOS 模拟器
  • Safari 调试器

问题描述

在一个结算列表页,发起审批流程,发起成功后再返回到该列表页,发现列表页的流程状态还是【未开始】, 这个涉及到了多项目间的跳转(甲方业务比较多,分项目开发会是一个趋势);另外,安卓没有这个问题
在这里插入图片描述

排查思路

按问题描述,操作一次,发现数据库状态已经改变, 但前端页面状态确实没变,刚开始以为是简单的 onLoad 和 onShow 问题。

实际上结算页和流程页实际是两个项目, 从流程项目返回结算项目后,不管页面请求写在 onLoad 还是 onShow 里,都应该会被重新执行才对; 可以从 Safari 中打开调试 WebView 的 inspector,来检查下前后端交互的过程
在这里插入图片描述
使用 location.reload() 重新加载页面, 可以看到应用和页面各自的日志(其中 page hide 是由 Vue 路由导航触发的), 操作发起审批,进入到流程项目
在这里插入图片描述

最后的 App LaunchApp Show 便是流程项目的日志,再使用 history.go(-2) 返回结算列表页
在这里插入图片描述
可以看到,虽然页面返回了结算列表页,但是并没有打印预期的 load 和 show 日志, 这些方法走不进去,写在 onShow 里请求接口再刷新状态的逻辑也就不会执行; 接下来想办法解决问题,如果能从 Web 解决的话最好,可以避免 App 更新版本

打断点调试,能看到 uni-app 是通过监听 visibilitychange 事件,来触发的 onShow 和 onHide (其实 onShow 的调用方不只一个,比如首次加载时、页面回退时,都有相对应的逻辑,但跟此处关系不大)。

查找这个事件的相关资料时,看到也有尝试用 pageshow1来解决其它类型的缓存问题,先用这个事件调试一下
在这里插入图片描述
测试以后,结果也不理想
在这里插入图片描述
从这个日志也能看出来,用 history.go(-2) 返回后,onLoad 方法确实没有触发, 但是 loaded 竟然又变成了 true (正常页面加载完以后,就把它变成了 false), 据此猜测,在离开一个系统时,IOS 把页面数据做了缓存,再返回这个页面时,又读取这些缓存做了渲染, 而且这种渲染不会触发上面的 visibilitychange 事件(dom api)事件, pageshow (window api) 虽然可以触发, 但是考虑到需要发起审批流程的页面会越来越多,这种写法比较复杂, 如果其他开发人员也要修改这个页面的其它逻辑,很可能带来困扰, 就放弃了这个思路(另外开发时热加载的刷新逻辑和正常逻辑也不太一样,所以还需要再兼容考虑这种情况)

还是得回到从 IOS 层面解决缓存的思路上

查找了一些资料后,判断这种情况可能是属于 WKWebsiteDataTypeMemoryCache(内存缓存), 刚好之前有调整过 WebView 的标题变化,凡是页面标题变化时,都会进入此方法, 便先把逻辑放在这里,经过验证,可以解决问题

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(
  NSDictionary<NSString *, id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"title"]) {
        if (object == self.webView) {
            self.title = self.webView.title;
            // clear memory cache
            NSSet *websiteDataTypes
                    = [NSSet setWithArray:@[
                            WKWebsiteDataTypeMemoryCache,
                    ]];
            NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
            [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes
              modifiedSince:dateFrom completionHandler:^{
                // done
                NSLog(@"------------clear---------");
            }];
        } else {
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        }

    }
}

微调

考虑到其实只有跨系统的时候才需要清除缓存,而大多数情况可能只是在本系统内跳转, 所以上面这个清除时机也不合适,最后突然想到了 decidePolicyForNavigationAction 这个方法

经过简单验证,这个方法大致可以满足需求:单项目内的跳转不会触发这个方法, 而跳转到其它项目时会正常触发(也算 Vue 这类单页应用的前端路由特性), 所以便先把清除逻辑放在了这里面来解决问题


  1. https://developer.mozilla.org/zh-CN/docs/Web/API/Window/pageshow_event ↩︎

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值