我是怎樣讓網站用上HTML5 Manifest

Manifest是用來作離線頁面的,即便斷網也能正常打開頁面,用起來簡單,可是在實際使用中存在如下問題:javascript

(1)如何自動緩存全部的頁面的資源?由於manifest不能使用*通配符進行cachephp

(2)若是網站資源更新,怎麼讓manifest文件自動更新?否則若是用戶不清緩存即便聯網也會加載老頁面css

我以爲不少網站沒有使用Manifest是由於上面提到的兩個緣由,有些人有嘗試過,但使用起來比較麻煩,離線應用價值好像不太大。可是使用Manifest仍是有不少好處的,特別是像博客等之類的偏向於展現的網站,或者是在線APP,這種網站的數據動態變化頻率比較低,不須要頻繁地向服務請求數據。這樣當用戶須要頻繁退回首頁或者頻繁地在幾個頁面來回切換的時候,因爲幾乎全部資源都在本地,因此加載起來是瞬時的。html

1. 使用Manifest

使用Manifest很簡單,就是在html標籤上加一個manifest屬性:html5

<html manifest="/static/manifest/home.appcache">複製代碼

這個屬性指向一個manifest的文件,這個文件指明瞭當前頁面哪些資源須要進行離線緩存,以下home.appcache:java

CACHE MANIFEST
#9/27/2017, 3:04:25 PM
#html
https://github.com/
#img
https://assets-cdn.github.com/images/modules/site/universe-octoshop.png
https://assets-cdn.github.com/images/modules/site/universe-wordmark.png
#css
https://assets-cdn.github.com/assets/frameworks-bedfc518345231565091.css
#js
https://assets-cdn.github.com/assets/compat-94eba6e3cd1fa18902d9.js
NETWORK:
*

FALLBACK
https://github.com/ /html/manifest/html/home.html複製代碼

這個文件第一行必須以CACHE MANIFEST開頭,不然瀏覽器解析會報錯,註釋使用#開頭,在這一行下面跟着須要緩存的資源,接着的NETWORK表示哪些資源須要聯網加載,通常須要寫成NETWORK *,表示除了CACHE外的其它全部資源都須要聯網,包括一些動態請求,若是你不是寫的*,而是寫了具體路徑,那些既沒有在CAHCE的,也沒有在NETWORK的就會報加載失敗的錯誤,以下所示:git

即便聯網也會這樣,因此通常寫成*。github

FALLBACK表示替代資源,這些資源加載不到就替代加載哪些資源,如上面的文件https://github.com訪問不了就使用一個靜態的html訪問:https://github.com/html/manifest/html/home.html。npm

打開支持Manifest的網站,例如www.rrfed.com,能夠觀察到Chrome控制檯cache的過程:跨域

而後再刷新頁面,你會發現頁面幾乎全部資源都是在本地緩存取的,以下圖所示:

而且你把網斷了,刷新頁面,頁面依舊可以正常加載出來。這個在Chrome/Firefox/Safari等瀏覽器均支持。

除了Manifest以外,還有另一個緩存的手段,就是設置HTTP報文頭的Cache-Control字段進行緩存,這個能夠緩存JS/CSS/圖片資源,可是若是你把HTML也緩存了就會有一個問題,若是用戶不清除緩存,即便你的頁面更新了,用戶仍然會加載老的頁面,直到緩存設定Max-Age時間到了。因此用Manifest能夠解決這個問題。

Manifest怎麼知道當前頁面數據更新了呢?只要把你把manifest文件如上面的home.appcache更改一下就能夠了,瀏覽器打開頁面時都會去加載這個文件,一旦發現這個文件發生了變化下次刷新的時候就會從新加載全部Cache的文件,最簡單的能夠把註釋裏的時間改爲當前的時間就能夠了:

#9/29/2017, 9:08:49 AM複製代碼

因此當網站的資源發生更改就能夠改變這個manifest的內容,進而聯網的瀏覽器就能進行更新。

使用Manifest須要注意如下問題:

(1)Manifest有大小限制,它其實也算本地存儲,本地存儲通常每一個域有限制使用的空間,PC Chrome是5Mb,參考以下表格

Browser Application Cache (AppCache) Storage Limit
Safari Desktop (Mac & Win) Unlimited
Safari Mobile (iOS) 10 MB
Chrome Desktop (Mac & Win) 5 MB *
Chrome Mobile (Android) Unlimited **
Firefox 4 Beta Unlimited (with user prompt)
IE No idea. It sucks. ***

(2)Manifest文件如home.appcahce不能跨域,若是跨域須要支持CORS

(3)Manifest Cache的資源不能跨域,一樣若是跨域該資源須要支持CORS,通常瀏覽器會自動處理

2. 解決Manifefst的自動生成和更新問題

因爲Manifest不能使用通配符匹配資源,因此須要把要進行cache的資源一個個列出來,而網站的內容常常是動態更新的,因此這個就比較麻煩。爲此筆者寫了一個自動生成manifest的NPM包generate-manifest,用起來很是簡單:

npm install -g generate-manifest
generate-manifest --url=https://github.com複製代碼

它就會生成一個home.appchache的Manifest文件,這個文件包括頁面上的img/js/css的資源連接:

CACHE MANIFEST
#9/27/2017, 3:04:25 PM
#html
https://github.com/
#img
https://assets-cdn.github.com/images/modules/site/universe-octoshop.png
https://assets-cdn.github.com/images/modules/site/universe-wordmark.png
#css
https://assets-cdn.github.com/assets/frameworks-bedfc518345498ab3204d330c1727cde7e733526a09cd7df6867f6a231565091.css
#js
https://assets-cdn.github.com/assets/compat-91f98c37fc84eac24836eec2567e9912742094369a04c4eba6e3cd1fa18902d9.js
NETWORK:
*

FALLBACK
https://github.com/ /html/manifest/html/home.html複製代碼

還能夠支持其它參數定製,詳見:generate-manifest

這樣就解決了自動生成的問題,自動更新應該怎麼辦呢?

因爲我是一個博客網站,網站內容發生變化的地方主要有:1. 發表/更改博客; 2. 用戶發表評論; 3. 網站的瀏覽量發生變化,第一個解決的方法寫了一個接口,只要發表博客就調一下這個接口去生成一個新的manifest文件:

https://www.rrfed.com/refresh-manifest.php?link=https://www.rrfed.com/2017/09/26/manifest/

而後就會調上面的generate-manifest的包,生成一個manifest.appcache的文件,在html裏面是根據路徑的最後一個部分決定manifest的名字:

<?php
    $uri = "$_SERVER[REQUEST_URI]";
    $uriArray = explode("/", $uri);
    $uriName = count($uriArray) > 2 ? $uriArray[count($uriArray) - 2] : "home";
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?> manifest=<?php echo "/html/manifest/appcache/$uriName.appcache"?>>複製代碼

這個和生成的文件名一一對應。

第二個問題:用戶發表評論——在調發表接口那裏自動地調一下這個接口,須要注意的是這個接口須要防腳本注入,否則比較危險,

第三個問題:閱讀量數據變化的——寫一個Linux定時任務,使用crontab添加一個定時任務,執行crontab -e添加:

0 3 * * * /home/fed/manifest/update-all.sh複製代碼

上面的意思是天天3:00的時候跑一下update-all.sh這個腳本,這個腳本把全部頁面的更新命令都寫進去:

generate-manifest --url=https://www.rrfed.com
generate-manifest --url=https://www.rrfed.com/page/2/
generate-manifest --url=https://www.rrfed.com/page/3/
#..其它...複製代碼

第一點提到的發表文章,也會添加一行命令到這個腳本里面。

因爲閱讀量這個數據不是很重要,因此一天更新一次就行了。這樣可讓用戶在同一天的操做有緩存。若是次日再來看的話就更新一下。

所以基本上就解決了自動更新的問題。

還有一個問題是,Manifest改了以後的第一次刷新仍是老的頁面,只有第二次刷新的時候纔是對的,因此咱們但願改了manifest以後可以一刷新就是新的,而不是以前緩存的那個,也不須要刷兩次。

那麼怎麼辦呢?Manifest有一個更新的事件,一旦manifest文件有更新就會觸發這個事件,因此咱們能夠監聽這個事件,而後自動刷新頁面讓頁面從新加載就能夠了,以下代碼:

function onUpdateReady() {
    window.location.reload(true);
}
window.applicationCache.addEventListener('updateready', onUpdateReady);
if(window.applicationCache.status === window.applicationCache.UPDATEREADY) {
    onUpdateReady();
}複製代碼


綜上,咱們很好地利用Manifest作了一個離線頁面應用,解決了自動生成和自動更新的問題。即便用戶沒有離線,第二次加載的資源都是在本地緩存的,因此當用戶在幾個頁面來回切換的時候這個速度是很快的,如不少人可能會在主頁的列表和內容頁之間來回切換。

雖然Manifest已經被deprecated了,被Service Worker取代了,可是因爲它的簡單易用以及兼容性好,咱們仍是能夠用一用。


相關閱讀:

  1. 爲何要把網站升級到HTTPS
  2. 怎樣把網站升級到http/2
相關文章
相關標籤/搜索