本文開源實驗室原創,轉載請以鏈接形式註明地址:https://kymjs.com/code/2017/02/18/01
題外話:寫給所有移動端開發的同學:PWA(Progressive Web Apps) 一定是將來的移動開發趨勢,且學且珍惜。
手機端可在我公衆號【技術實驗室】的歷史推送文章查看。
已經公開章節:
第一篇 · Service Worker:讓網頁無網絡也能訪問
第二篇 · PWA 程序開發實踐
GitHub 演示項目 · PWAblog
介紹
這裏再介紹一下 Progressive Web Apps 是結合了 web 和 原生應用中某些功能的一種體驗(本質上還是 web 應用)。但是作爲一個 web 應用,它可以 斷網使用、推送消息、發送通知、從桌面啓動,當然還包括 Web 應用的優勢:免安裝、快速開發、依賴瀏覽器跨平臺(支持包括Edge在內的各種主流PC/手機瀏覽器)。
fetch 攔截請求
之前講述了 PWA 最重要的組件:Service Worker,沒有看過的可以先看看:https://kymjs.com/code/2017/02/04/01/。今天繼續來看他的一些高級屬性。
今天要說的高級屬性已經寫在了我的Service Worker裏,可查看:開源實驗室防盜鏈 中fetch
事件。
fetch
是一個很有意思的事件,它可以攔截一個網絡請求數據,決定是從緩存返回還是去網絡請求。
self.addEventListener('fetch', function(e) {
var allDataUrl = filesToCache;
var requestIsDataApi = false;
for (dataurl in allDataUrl){
if (e.request.url.indexOf(dataurl) > -1 ) {
requestIsDataApi = true;
e.respondWith(
caches.open(dataCacheName).then(function(cache) {
return fetch(e.request).then(function(response){
cache.put(e.request.url, response.clone());
return response;
});
})
);
break;
}
}
if (!requestIsDataApi){
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
}
});
這裏,我將所有請求的 URL 做了個判斷,如果是緩存URL,直接從本地緩存中返回;反之如果是不屬於緩存 URL,就攔截請求,使用fetch()
發請求,將結果保存到緩存並返回。
可以攔截請求這一點讓fetch
事件可以玩出花來,下一篇我們繼續講 PWA 時再看它的黑科技。
應用程序外殼(App Shell)
如果你按照上一篇博客的講述,自己動手實現了博客斷網訪問。在欣喜的同時一定也會發現,博客沒法更新了。
當博客內容更改的時候,如果之前用戶已經訪問過這篇博客,他再次訪問依舊是之前緩存過的內容,而不是新內容。
這就涉及到 PWA 的一個名詞:應用程序外殼(App Shell)。
一個 web 應用分爲 應用程序外殼 和 應用數據, 應用外殼的結構分爲應用的核心基礎組件和承載數據的 UI。所有的 UI 和基礎組件都使用一個 service worker 緩存在本地,因此在後續的加載中 Progressive Web App 僅需要加載需要的數據,而不是加載所有的內容。
這就類似 Android 應用,下載安裝的是外殼,只需要下載一次,接口API請求的數據是實時變化的。
因此,之前我們是把整個博客當成了APP Shell
,除非版本變更,否則當然不會再發生變化。
設計 App Shell
還是以 開源實驗室 來做例子,首先看看效果圖。
一個移動應用,應該是包括 ActionBar(StatusBar)
、Content
、BottomBar(FooterBar)
三部分構成。ActionBar 和 BottomBar 基本是固定的不會有大變化,這部分用 html 很容易實現,最主要的是 Content 部分,這部分內容是動態改變的,不包括在 App Shell
中,因此這裏我們先不管他。
<!DOCTYPE html>
<html>
<head>
<title>開源實驗室-kymjs張濤</title>
<link rel="stylesheet" type="text/css" href="styles/inline.css">
</head>
<body>
<header class="header">
<h1 class="header_title">開源實驗室</h1>
<a href="https://kymjs.com/about"><button id="butAbout" class="headerButton" aria-label="about"></button></a>
</header>
<main class="main">
</main>
<footer class="footer">
<button id="column1" class="button1">專欄</button>
<button id="column2" class="button2 active">博客</button>
<button id="column3" class="button3">作品</button>
</footer>
<script src="scripts/app.js" async></script>
</body>
</html>
APP Shell 已經完成了,接下來我們需要在 service-worker 中聲明他是一個不需要多次下載的頁面。
設計 Content
由於 Content 是變化的,所以我們只寫一個模板,讓 js 去根據數據改變這個模板內容。
先寫好將會放在 Content 區域的 item 模板(相當於 Android 中 ListView 的 item 先創建一個模板)。
<div class="post-list-body post-area">
<div class="list-item" hidden>
<a href="http://kymjs.com/" id="blogLink">
<div class="post-list-item">
<font color="#aaaaaa" id="blogTime"></font>
<h2><font color="#AE57A4" id="tag"></font>
<font color="#333333" id="title"></font></h2>
<p><font color="#666666" id="description"> description template </font></p>
</div>
</a>
</div>
</div>
接着使用 AJAX 請求博客的內容數據,可以通過調用開源實驗室的 OpenAPI 獲取數據。
app.requestBlogList = function(){
//跨域請求會失敗,本地調試時先使用僞數據
// var url = "/download.json";
var url = "http://openapi.kymjs.com/oslab";
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState == XMLHttpRequest.DONE && xmlhttp.status==200){
var response = JSON.parse(xmlhttp.response);
var itemList = response.item;
app.addContent(itemList);
}
};
xmlhttp.open("GET", url);
xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;");
xmlhttp.send();
}
拿到數據了以後,調用addContent()
,去根據模板動態生成內容。代碼就不貼了,具體可查看 github 倉庫 PWAblog。
配置 Service Worker
至此,整個應用就已經全部開發完成了,接下來我們只需要配置 service worker 讓應用程序外殼生效就 OK 了。
回顧上一篇文章,首先在 js 中判斷瀏覽器是否支持,如果支持就註冊。
監聽 install
事件,緩存 url。
監聽 activate
事件,移除過期緩存,保證應用更新版本後可以升級。
最後,通過fetch
事件攔截網絡請求做訪問緩存邏輯處理。
這樣一個簡單的 PWA 程序就完成了。