Weex缓存管理和实现

本文阅读的前提是您对Weex已有所了解.
本文的缓存管理主要是由Native端去实现的(99%), Web端可能需要配合做相应的处理(1% 后面会提到).
本文的缓存管理是基于我对Weex的理解而建立的.

前言:
在做缓存之前也是网上看了很多资料, 大体的思路就是将所有JS打包成JS Bundle下载到本地, 通过服务端返回的ETag去判断是否有更新, 决定是否更新本地JS Bundle.
参考资料:
Weex的JS缓存实现 - Android
可能是史上最全的weex踩坑攻略
但是我觉得没有必要将JS打包成Bundle统一下载, 一是慢, 二是如果某一个页面发生改动则需要连同下载所有的JS, 这样不是很好~

首先说下我的缓存思路~

在WeexSDK加载Url之前(RenderUrl), 对这个Url进行缓存处理和判断, 针对页面进行缓存处理和更新判断
比如iOS代码~:

// 拿到要渲染的http://xxx.js路径 在渲染前对这个js进行处理
[[WeexCacheManager defaultManager] getRenderUrlWithRequestUrl:self.urlString callBack:^(NSURL *URL) {
            dispatch_async(dispatch_get_main_queue(), ^{
                // WeexSDK 渲染 js 
                [self.wxInstance renderWithURL:URL options:@{@"bundleUrl":URL.absoluteString} data:nil];
            }); 
        }];

这样的话会很方便管理缓存~

缓存思路

目的:

1.仅对.js文件进行缓存处理, 降级后显示的.html无需考虑~

2.将服务端.js文件下载保存至本地沙盒, Weex管理并只加载缓存JS文件

3.若从服务端下载.js文件失败则不考虑加载缓存,直接渲染网络文件

基础配置:

1.在做缓存管理之前. 我是将JS文件放在服务端的, 本地渲染的是服务端上的JS地址.

2.图片静态资源也都是放到服务端部署的. 所以不存在加载Native端的图片

3.页面跳转我是分开写的,如果是Web环境使用Weex自带的Navigator.Push(), 否则我是在Native中注册跳转的Module, 实现Native端可控的跳转方法.JS代码如下:

// push操作
    $push (path) {
      if (this.$isNativePlateform() && weex.supports && weex.supports('@module/navigation.push')) {
        weex.requireModule('navigation').push({
          h5Url: this.$getPushPath(true, path),
          fallbackUrl: this.$getPushPath(false, path),
          animated: true
        })
      } else {
        weex.requireModule('navigator').push({
          url: this.$getPushPath(this.$isNativePlateform(), path),
          animated: 'true'
        })
      }
    },

navigation是我在Native端自己注册的方法, 方法也就是创建一个自己的WeexViewController, 当前页面Push过去~ 我的这篇文章有关于页面跳转的思路说明

4.跳转参数传递

  goOrderDetail (status) {
      if (status || this.isOrderList) {
        this.$push('views/order/order-detail?applyId=' + this.dataItem.applyId)
      }
    }

上面JS代码可以看到我跳转页面的参数传递, 我只传了页面的相对地址和需要的参数,在真正跳转之前我会对这个path进行处理:

$getPushPath (path) {
 // 用于处理path, 返回正确完整的Path, 主要工作是判断当前环境是否是Web环境, 如果是则返回.html + Query,  否则就是 .JS+Query 的形式
}

具体实现:

先看下获取到的完整的JS路径地址:
http://baidu.com/blife-weex/dist/views/home/home.js?tokenServer=27abb15db479989f841c365ebbd&appDeviceid=e7212FF79-80-EC88D156AE95&sourceType=1&fromWhere=APP&appType=1&moduleCd=Home&inApp=1&refType=&refId=&version=1.0.0

这个地址也就是对应我们写的一个Vue页面~ 参数部分暂不考虑, 需要保存的文件名称应该是:(URL.scheme + “://“ + URL.host + URL.relativePath 再去掉.js后缀)

http://baidu.com/blife-weex/dist/views/home/home

这就能保证文件名(地址)的唯一性了, 因为需要支持文件系统命名, 所以我将.js前面的部分用MD5转换一下, 这样文件名称就变成了 0d86bb3022272b658d2b14374173e497.js
只要是这个地址, 所生成的MD5就是一样的! 这也就是我们下在下来需要缓存的文件

保存在本地沙盒中某一指定的位置吧~

下一次再访问这个地址, 如果发现本地存在这个MD5.js的文件就说明之前已经做了缓存了~ 如果没有当然就下载这个.js文件 以MD5.js形式保存就行了

接下来考虑下得到了已经缓存的本地JS后改怎么办~

我们需要判断服务端的JS是否有更新, 如果更新了我们需要删除本地JS并重新下载新的JS并缓存到本地,再加载最新的JS文件, 方法是通过Headers中的ETag值.
ETag: 可以理解成服务端文件对应的hash,当文件内容变动了这个hash也会随之变化,ETag也会变, ETag值存在请求服务端文件(.js)的网络请求返回的Headers里
这样的话我们就需要存储ETag值去标识对应的缓存JS文件了~

我的做法是在当前沙盒目录中再创建一个Plist/Map本地文件, 该Plist/Map文件只有一个, 构造是Dictionary/Map套Dictionary/Map形式,
最外层Dictionary/Map以存储的MD5(下载的JS文件名)作为Key, Value是一个Dictionary/Map, 这个子Dictionary/Map用于存Etag等值,

这样的话在之前下载JS完成后, 存储下载JS的请求的ETag值, 作为下次加载JS前判断服务端是否更新的参照.

所以再加载本地JS前, 先发送一个Header请求, 拿到ETag值之后,通过Key/Value的形式找到本地存储的Plist/Map中对应的MD5的对象, 再拿到上次请求JS的ETag标识, 如果不一样则说明服务端更新了,需要重新下载~.

最后在只需要将本地JS路径拼接上原URL的Query部分 返回给Weex让其加载就可以了~
大致是这样的URL:


file:///User/xxx/0d86bb3022272b658d2b14374173e497.js?tokenServer=27abb15db479989f841c365e

以上就是基本的缓存策略~

额外扩展:

1.简化请求次数

在上述基本的缓存策略中, 我略加修改了一些东西 , 再下载后存储ETag值的同时, 我同时保存了下当前的时间戳(秒), 我这边设置了两个小时(某一时间)内,不用判断服务端是否更新, 不然的话每一次加载本地JS我都要去发送Header请求去判断服务端更新这样并不是很好~. 所以在加载本地JS时我判断如果上次存储的时间在两个小时(某一时间)内, 则不发送Header请求, 直接加载本地JS. 如果超过两个小时,则发送Header请求, 但如果发现服务端ETag值没变 ,则更改这个时间戳为当前时间戳, 保证下一个两小时(某一时间)内依然不发送Header请求, 直接加载.

2.关于获取域名和资源加载问题

其实一般都会出现关于域名更变的情况, 因为都会有测试服,正式服的部署.或者说在加载图片的时候都是通过当前域名加相对途径的形式去加载, 页面跳转也是这样,所以这就会遇到一个问题:
在加载本地JS资源时,路径都是file://的形式,没有办法获取到真正的域名地址

解决方法:

同样在下载JS保存ETag值的同时,保存下当前URL的域名, 在返回本地JS完整路径的时候将这个域名以一个参数的形式拼接在Query里传给前端, 这样前端就可以通过获取Query里的参数判断出用什么域名去显示静态资源了(1%的修改在这里~)

3.关于加载本地JS缓存后 weex-ui 中 wxc-icon组件显示错误
解决方法:

我在这里提了issue, 我是Copy了他们的源码放在本地,改了下请求就好了

iOS的小礼物

我将整理好的缓存管理类上传到GitHub(记得点个✨)了, iOS的同学可以直接用或者修改, Android的...我不会哈

PS:以上思路可能不是最好哈, 如果有可以改进的地方还请留言交流讨论

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342