admin 管理员组

文章数量: 1086019

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 层面解决缓存的思路上

  • Data Store Record Types
  • iOS - 清除Web界面的缓存

查找了一些资料后,判断这种情况可能是属于 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 cacheNSSet *websiteDataTypes= [NSSet setWithArray:@[WKWebsiteDataTypeMemoryCache,]];NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypesmodifiedSince:dateFrom completionHandler:^{// doneNSLog(@"------------clear---------");}];} else {[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];}}
}

微调

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

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


  1. ↩︎

本文标签: IOS WebView 的返回缓存问题分析