本文閱讀的前提是您對Weex已有所了解.
本文的緩存管理主要是由Native端去實現(xiàn)的(99%), Web端可能需要配合做相應的處理(1% 后面會提到).
本文的緩存管理是基于我對Weex的理解而建立的.
前言:
在做緩存之前也是網(wǎng)上看了很多資料, 大體的思路就是將所有JS打包成JS Bundle下載到本地, 通過服務端返回的ETag去判斷是否有更新, 決定是否更新本地JS Bundle.
參考資料:
Weex的JS緩存實現(xiàn) - Android
可能是史上最全的weex踩坑攻略
但是我覺得沒有必要將JS打包成Bundle統(tǒng)一下載, 一是慢, 二是如果某一個頁面發(fā)生改動則需要連同下載所有的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文件失敗則不考慮加載緩存,直接渲染網(wǎng)絡文件
基礎(chǔ)配置:
1.在做緩存管理之前. 我是將JS文件放在服務端的, 本地渲染的是服務端上的JS地址.
2.圖片靜態(tài)資源也都是放到服務端部署的. 所以不存在加載Native端的圖片
3.頁面跳轉(zhuǎn)我是分開寫的,如果是Web環(huán)境使用Weex自帶的Navigator.Push(), 否則我是在Native中注冊跳轉(zhuǎn)的Module, 實現(xiàn)Native端可控的跳轉(zhuǎn)方法.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端自己注冊的方法, 方法也就是創(chuàng)建一個自己的WeexViewController, 當前頁面Push過去~ 我的這篇文章有關(guān)于頁面跳轉(zhuǎn)的思路說明
4.跳轉(zhuǎn)參數(shù)傳遞
goOrderDetail (status) {
if (status || this.isOrderList) {
this.$push('views/order/order-detail?applyId=' + this.dataItem.applyId)
}
}
上面JS代碼可以看到我跳轉(zhuǎn)頁面的參數(shù)傳遞, 我只傳了頁面的相對地址和需要的參數(shù),在真正跳轉(zhuǎn)之前我會對這個path進行處理:
$getPushPath (path) {
// 用于處理path, 返回正確完整的Path, 主要工作是判斷當前環(huán)境是否是Web環(huán)境, 如果是則返回.html + Query, 否則就是 .JS+Query 的形式
}
具體實現(xiàn):
這個地址也就是對應我們寫的一個Vue頁面~ 參數(shù)部分暫不考慮, 需要保存的文件名稱應該是:(URL.scheme + “://“ + URL.host + URL.relativePath 再去掉.js后綴)
http://baidu.com/blife-weex/dist/views/home/home
這就能保證文件名(地址)的唯一性了, 因為需要支持文件系統(tǒng)命名, 所以我將.js前面的部分用MD5轉(zhuǎn)換一下, 這樣文件名稱就變成了 0d86bb3022272b658d2b14374173e497.js
只要是這個地址, 所生成的MD5就是一樣的! 這也就是我們下在下來需要緩存的文件
保存在本地沙盒中某一指定的位置吧~
下一次再訪問這個地址, 如果發(fā)現(xiàn)本地存在這個MD5.js的文件就說明之前已經(jīng)做了緩存了~ 如果沒有當然就下載這個.js文件 以MD5.js形式保存就行了
接下來考慮下得到了已經(jīng)緩存的本地JS后改怎么辦~
我們需要判斷服務端的JS是否有更新, 如果更新了我們需要刪除本地JS并重新下載新的JS并緩存到本地,再加載最新的JS文件, 方法是通過Headers中的ETag值.
ETag: 可以理解成服務端文件對應的hash,當文件內(nèi)容變動了這個hash也會隨之變化,ETag也會變, ETag值存在請求服務端文件(.js)的網(wǎng)絡請求返回的Headers里
這樣的話我們就需要存儲ETag值去標識對應的緩存JS文件了~
我的做法是在當前沙盒目錄中再創(chuàng)建一個Plist/Map本地文件, 該Plist/Map文件只有一個, 構(gòu)造是Dictionary/Map套Dictionary/Map形式,
最外層Dictionary/Map以存儲的MD5(下載的JS文件名)作為Key, Value是一個Dictionary/Map, 這個子Dictionary/Map用于存Etag等值,
這樣的話在之前下載JS完成后, 存儲下載JS的請求的ETag值, 作為下次加載JS前判斷服務端是否更新的參照.
所以再加載本地JS前, 先發(fā)送一個Header請求, 拿到ETag值之后,通過Key/Value的形式找到本地存儲的Plist/Map中對應的MD5的對象, 再拿到上次請求JS的ETag標識, 如果不一樣則說明服務端更新了,需要重新下載~.
最后在只需要將本地JS路徑拼接上原URL的Query部分 返回給Weex讓其加載就可以了~
大致是這樣的URL:
file:///User/xxx/0d86bb3022272b658d2b14374173e497.js?tokenServer=27abb15db479989f841c365e
以上就是基本的緩存策略~
額外擴展:
1.簡化請求次數(shù)
在上述基本的緩存策略中, 我略加修改了一些東西 , 再下載后存儲ETag值的同時, 我同時保存了下當前的時間戳(秒), 我這邊設置了兩個小時(某一時間)內(nèi),不用判斷服務端是否更新, 不然的話每一次加載本地JS我都要去發(fā)送Header請求去判斷服務端更新這樣并不是很好~. 所以在加載本地JS時我判斷如果上次存儲的時間在兩個小時(某一時間)內(nèi), 則不發(fā)送Header請求, 直接加載本地JS. 如果超過兩個小時,則發(fā)送Header請求, 但如果發(fā)現(xiàn)服務端ETag值沒變 ,則更改這個時間戳為當前時間戳, 保證下一個兩小時(某一時間)內(nèi)依然不發(fā)送Header請求, 直接加載.
2.關(guān)于獲取域名和資源加載問題
其實一般都會出現(xiàn)關(guān)于域名更變的情況, 因為都會有測試服,正式服的部署.或者說在加載圖片的時候都是通過當前域名加相對途徑的形式去加載, 頁面跳轉(zhuǎn)也是這樣,所以這就會遇到一個問題:
在加載本地JS資源時,路徑都是file://的形式,沒有辦法獲取到真正的域名地址
解決方法:
同樣在下載JS保存ETag值的同時,保存下當前URL的域名, 在返回本地JS完整路徑的時候?qū)⑦@個域名以一個參數(shù)的形式拼接在Query里傳給前端, 這樣前端就可以通過獲取Query里的參數(shù)判斷出用什么域名去顯示靜態(tài)資源了(1%的修改在這里~)
3.關(guān)于加載本地JS緩存后 weex-ui 中 wxc-icon組件顯示錯誤
解決方法:
我在這里提了issue, 我是Copy了他們的源碼放在本地,改了下請求就好了
iOS的小禮物
我將整理好的緩存管理類上傳到GitHub(記得點個?)了, iOS的同學可以直接用或者修改, Android的...我不會哈
PS:以上思路可能不是最好哈, 如果有可以改進的地方還請留言交流討論