本文是一個PWA的入門教程,也算是一個introduction(導讀),不會太深入 主要聊聊三個重點: PWA的本質,構成PWA的兩個文件,PWA APP如何更新 PWA的本質 Q: 網站和原生app在存儲上的本質區別是什麼? A: 網站中使用的素材都是通過請求實時獲取的, app中使用的素材都是本 ...
本文是一個PWA的入門教程,也算是一個introduction(導讀),不會太深入
主要聊聊三個重點: PWA的本質,構成PWA的兩個文件,PWA APP如何更新
PWA的本質
Q: 網站和原生app在存儲上的本質區別是什麼?
A: 網站中使用的素材都是通過請求實時獲取的, app中使用的素材都是本機中存儲的。在離線或者素材過大的情況下,前者每次訪問都要載入,載入過慢的缺點就暴露了出來。離線使用體驗永遠大於線上使用體驗
有了這個前置知識之後,補充一個事實: chrome在版本43, 2015年5月的時候提供了chache API
而cache API就是PWA的魔法: 通過預先下載素材緩存到cache里,下次打開就不用經過網路請求,實現離線的效果
個人感覺這樣解釋應該很清晰了,所以下麵進入實際操作環節:
兩個文件
從錶面上看,要將一個網站轉成PWA網站,只需要添加兩個文件: manifest.json和service-worker.js
前者是對APP的說明,後者是我們進行cache操作的地方
manifest.json
在這個配置文件里我們可以定義:
- name: APP的名稱
- short_name: 被添加到桌面(相當於安裝)後, app圖標下麵的名稱
- start_url: 打開應用時所處URL
- display: 指定成standlone就能顯示的跟原生APP一樣(沒有瀏覽器導航欄之類的東西)
其他還有背景顏色和圖標之類的,都是些簡單的屬性在MDN上查一下就完事了: https://developer.mozilla.org/en-US/docs/Web/Manifest
service-worker.js(簡稱sw)
要用sw,首先要在index.html里對sw進行註冊
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('service worker registered'))
.catch(err => console.log('service worker not registered', err));
sw相當於一個安裝器+攔截器,簡單說下這個安裝過程是怎麼實現的: 每次執行navigator.serviceWorker.register('/sw.js')
都會重新下載sw.js這個文件,就像每次打開網路游戲都要先檢查更新登陸器一樣, 然後登陸器會告訴我們是不是要下載安裝補丁包。如果新下載的sw.js跟之前稍微有點不一樣,我們的網站都會被認為是一個新版本,然後重新觸發install事件(在sw.js裡面監聽), 反之如果文件完全一樣就不會觸發install事件. 就像更新結束之後網游會要求用戶重啟游戲一樣, install結束之後,我們進入一個等待狀態,只有用戶重新打開網站,我們才會進入激活狀態觸發activate事件(和install事件一樣activate事件一個版本只觸發一次).
請仔細閱讀上面這個描述,我已經儘可能用簡單易懂的方式去描述了。上面提到的兩個事件install和activate是最重要的,前者我們通常新建一個cache,然後預載入素材。用戶重啟網站後,在activate事件里我們通常刪除上個版本的cache。
下麵是無聊的代碼(占了整個sw文件的三分之二),就是做了上述的兩件事情, 不想看就跳過吧
const staticCacheName = 'site-static-v1';
const assets = [
'/',
'/index.html?v=4',
'/assets/js/ui.js',
'/assets/css/main.css',
'/assets/images/background-home.jpg',
'https://fonts.googleapis.com/css?family=Lato:300,400,700',
];
// install event
self.addEventListener('install', evt => {
evt.waitUntil(
caches.open(staticCacheName).then((cache) => {
console.log('caching shell assets');
cache.addAll(assets);
})
);
});
// activate event
self.addEventListener('activate', evt => {
evt.waitUntil(
caches.keys().then(keys => {
return Promise.all(keys
.filter(key => key !== staticCacheName)
.map(key => caches.delete(key))
);
})
);
});
在sw里,我們主要做的事情有兩件:
- 管理cache(增刪查改)
- 攔截網路請求, 從而實現像動態cache之類的騷操作
攔截網路請求就是剩下的三分之一代碼, 針對一個請求我們先檢查cache裡面有沒有,如果有就直接返回,沒有就繼續請求:
self.addEventListener('fetch', evt => {
evt.respondWith(
caches.match(evt.request).then(cacheRes => {
return cacheRes || fetch(evt.request);
})
);
});
上面就是整個sw的代碼和工作原理
用谷歌教程上的話來說,sw的生命周期是pwa最大的難點, 全部的工作原理我都在上面說明瞭, 如果看明白了那你就是掌握了pwa最難的部分,接下來我再總結一下(三個狀態):
- 預載入素材時的安裝中狀態installing
- install完之後, 新的sw + chache沒有立即生效,而是進入等待用戶重新打開網站的waiting狀態
- 用戶重新打開網站之後,進入激活狀態activate
PWA APP如何更新
從上面sw的工作原理部分可以得知,sw.js是每次打開網站都會重新載入的!
所以想要更新網站很簡單,只要更改sw.js(比如加上一行新的素材路徑), 然後在install事件里新開一個cache,然後在activate事件里刪除舊版本cache就OK了
比如上面的代碼, activate事件里已經做了刪除舊cache的操作(如果cache和當前版本cache名不一樣就刪掉),所以只要更改staticCacheName就可以了