瀏覽器的緩存(2)

親,若是你還在爲你沒網打開不網頁而煩惱嗎?
親,你還在爲你web服務器複雜的配置項而蛋疼嗎?
不要998,manifest抱回家~
manifest自H5橫空出世以來給前端網頁的瀏覽帶來了翻天覆地的變化,之前咱們的網頁必須在有網的前提下打開(主要仍是打開HTML), 可是如今,咱們能夠offline 瀏覽。 能夠算是實現web app的一個特技。
manifest的兼容性 IE9+. 因爲是現代的技術,IE9如下的古老瀏覽器是不支持的。因此,manifest主要應用是針對現代瀏覽器或者手機端更多一些。javascript

入門manifest

瀏覽器檢測你是否使用manifest特技時,是檢測html標籤.css

<html lang="en" manifest="usable.manifest">

當解析你的HTML時,發現存在manifest文件時,則會進行以下的操做:
manifest加載
(from alloy team)
manifest文件能夠是任意後綴好比. usable.manifest||usable.mf等,可是他的MIMEtype必須設置正確.
記住,這個時候manifest會將HTML文件也一併保存,這須要注意。html

書寫manifest文件

一個簡單的demo:前端

CACHE MANIFEST
#version 1.3
/public/static/index.css
/public/static/header.css
NETWORK:
*
FALLBACK:
/userInfo/ /404.html
#額外須要添加的緩存文件
CACHE:
images/logo1.png
images/logo2.png

基本樣式就是上述java

CACHE MANIFEST/CACHE

第一行必須是指定頭即, "CACHE MANIFEST"(不能有其餘的). 表示哪些文件須要緩存。若是是相對路徑則是,在manifest文件所在的目錄下。並且,不能使通配符!!!(tm 你是還不是傻). 因此通常而言只能一個一個配置.nginx

CACHE MANIFEST
#相對於manifest文件所在的目錄
    ./index.css

註釋: 註釋使用#+"info"
能夠對緩存文件性質進行適當的說明。緩存後的文件,就會被帶上Expires的頭,表示能夠不通過服務器驗證直接使用本地文件。因此,返回status Code 爲 200.
另外,CACHE 定義的文件內容,和CACHE MANIFEST 是一個效果,只是跟在CACHE MANIFEST以後,就能夠省略書寫CACHE,你添加上也能夠。git

CACHE MANIFEST
#version 1.3
CACHE:
/favicon.ico

並且CACHE能夠放在文中的任意位置,不過通常都是放開頭,或者省略.github

CACHE MANIFEST

# 緩存文件
index.html
css/style.css

NETWORK:
*

# 額外的須要緩存的文件
CACHE:
images/logo1.png
images/logo2.png
images/logo3.png

NETWORK

這裏設置不使用緩存的文件,可使用通配符"*"等。
* 表示,除了CACHE MANIFEST定義的文件以外的文件都不能被緩存。
固然也能夠手動指定文件:web

NETWORK
*
http://www.example.com/index.html
http://www.example.com/header.png
http://www.example.com/blah/blah

這些瀏覽器都不能直接使用緩存,即,可能會要求你從新驗證,或者直接使用服務器文件。ajax

FALLBACK

這個tag,可用可不用。 用來表示,指定文件沒法加載時,使用另外的文件代替。參數有兩部分構成,第一部分是指定資源(可能存在文件未加載),第二部分是替代資源

FALLBACK:
/index.html /404.html
/static/* /404.html
/images/* /NotFound.jpg

當index.html沒法加載時,使用404.html代替. 這裏有個要求,兩個路徑必須使用相對路徑而且與清單文件同源。

SETTINGS

這算是一個附加屬性吧。一般設置內容就只有:

SETTINGS:
prefer-online

表示,在有網的狀況下,會先訪問服務器的文件,看有沒有更新,至關於設置了Cache-Control:max-age=0,must-revalidate; + ETag||Last-modified. 不過,比較stupid的是,只有FF(Opera 12)支持.

服務器設置manifest

而在服務器端,須要對manifest文件的MIME設置正確。這裏以nginx爲例, 具體設置一下MIME type

type{
  image/gif                             gif;  
  image/jpeg                            jpeg jpg;  
  application/x-javascript              js;  
  }

詳情能夠參考: manifest文件配置

自動生成manifest文件配置

這裏以gulp爲例。 能夠在npm裏面很容易找到gulp-manifest這個生成插件.
直接下載:

npm install gulp-manifest --save-dev

而後在gulpfile裏面配置:

gulp.task('manifest', function(){
  gulp.src(['build/**'], { base: './' })
    .pipe(manifest({
      hash: true,
      preferOnline: true,
      network: ['*'],
      fallback:['/images/* /404.html']
      filename: 'app.manifest',
      exclude: 'app.manifest'  //不保存manifest,不過有沒有效果同樣
     }))
    .pipe(gulp.dest('./'));
});

接着就會在目錄下生成app.manifest文件,裏面就是一些基本的文件格式了。另外若是你想查看你電腦有多少網頁是manifest,能夠直接訪問 chrome://appcache-internals/.

manifest的坑點

manifest對於單頁應用可謂是如魚得水,可是,到了多頁應用的層面,他的bug真的是暴露無遺。

1.頁面保存的複雜度,
2.文件的及時更新,
3.緩存文件的設置, 
4.死都會保存HTML,
5.文件下載出錯,則此次更新緩存失敗,
6.覆蓋全部緩存頭,除了Cache-Control:no-store
7.在Android 4.4的webview裏,關閉以後會丟失cache
8.IE10不能很好的支持FALLBACK部分.

因此,appCache的bug也是很是多的。
例如,長尾更新問題,當你的頁面保持在線的時候,是沒法檢測文件已經更新,除非你reload頁面,可是用戶並不知道你已經更新,因此這裏咱們須要引進js的提供的緩存檢測API.

window.applicationCache

這是前端可以摸到緩存最真實的API。咱們能夠經過這個API接口獲取到咱們不少想要的東西:

var appcache = window.applicationCache;
console.log(appcache.status); //檢查當前緩存狀態
console.log(appcache.IDLE); //緩存狀態常量,下面解釋

經常使用的屬性有:

屬性名 explanation
status 當前緩存狀態,爲Number類型. 爲0~5
UNCACHED(0) 瀏覽器未緩存文件
IDLE(1) 空閒狀態,瀏覽器已經所有緩存
CHECKING(2) 頁面正在檢查當前離線緩存是否須要更新
DOWNLOADING(3) 頁面正在下載須要更新的緩存文件
UPDATEREADY(4) 頁面緩存更新完畢
OBSOLETE(5) 緩存已通過期

經常使用的方法:

window.applicationCache.update()  
//update方法調用時,頁面會主動與服務器通訊,檢查頁面當前的緩存是否爲最新的,如不是,則下載更新後的資源

window.applicationCache.swapCache()  //updateready後,更新到最新的應用緩存

一般結合上述兩個方法和相應的屬性咱們能夠手動觸發文件的更新(前提是 manifest文件改動).

var appCache = window.applicationCache;

appCache.update(); 
//檢查更新

if (appCache.status == window.applicationCache.UPDATEREADY) { 
//若是存在更新,而且已經下載ok,則替換瀏覽器緩存
  appCache.swapCache();  
}

可是,此時頁面並不能用上最新的文件,只是瀏覽器的緩存已經改變,網頁實際內容仍是原來的內容,還須要手動進行reload,才能進行更新文件

window.addEventListener('load', function(e) {

  window.applicationCache.addEventListener('updateready', function(e) {
    if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
      if (confirm('文件有更新,手否從新加載文件')) {
        window.location.reload();
      }
    } else {
      //若是,拒絕則不刷新網頁
    }
  }, false);

}, false);

cache相關事件

相關事件有: checking,downloading,updateready,obsolete,cached,error,noupdate,progress.
對照上述的status就能夠很容易知道每一個事件對應的效果是神馬。 須要說的就是:

progress: 當瀏覽器在下載資源時,每下載成功一次,就會觸發一次
noupdate:當瀏覽器檢查更新以後發現沒有資源更新的時候觸發這個事件
error: 更新出錯時會觸發,好比文件沒法正常下載,manifest文件被刪除.

其實,使用manifest的時候,無外乎就是3種經常使用狀態

  1. 第一次訪問頁面時

  2. 再次訪問頁面時,沒有更新

  3. 再次訪問頁面時,有更新

每次,觸發的事件順序爲:

行爲 事件順序
第一次訪問頁面 checking->downloading->progress(屢次)->cached
再次訪問時,沒有更新 checking->noupdate
再次訪問時,有更新 checking->downloading->progress(屢次)->updateready

上面看不懂不要緊,咱們能夠看看更直觀的Console的內容。

  1. 第一次訪問頁面時

checking->downloading->progress(屢次)->cache

2\. 再次訪問頁面時,沒有更新

checking->noupdate

3\. 再次訪問頁面時,有更新

checking->downloading->progress(屢次)->updateready

淺談manifest

其實,manifest就是爲了離線應用而生的,可是因爲設計之初,沒有很好的規範,致使如今manifest的bug,真的超級多。
看到whatwg上面說的一句話,真的更加蛋疼.

This feature is in the process of being removed from the Web platform. (This is a long process that takes many years.) Using any of the offline Web application features at this time is highly discouraged. Use service workers instead.

意思就是讓你不要用manifest,應該他早晚要被fire的,可是,這一天還有不少年,不少年。 另一個替代方案就是使用SS,可是兼容性,真的極其差。幾乎如今的瀏覽器都沒有實現(除了佈道師FF實現了部分). 如今咱們真的很尷尬,不過,目前的狀況而言,in my opinion, 是十分推薦使用的(也沒有其餘的辦法了). 那該怎麼作,才能將manifest的Bug減到最低呢?推薦的作法是將邏輯頁面和用戶數據給分離開。 邏輯頁面使用app cache,而用戶數據能夠保存在web Storage || indexDB 等瀏覽器數據庫裏,動態更新data時,使用web Socket,ajax,SSE等技術.

相關文章
相關標籤/搜索