摘要:原文地址譯文出自我的個人博客概述在本文中,您將學習如何使用和創(chuàng)建離線優(yōu)先數據驅動的漸進式應用程序。在離線的情況下也可以使用后臺同步功能將應用程序與服務器同步。保存數據到中現在我們保存數據到剛創(chuàng)建的數據庫中的對象中。
原文地址:Build an offline-first, data-driven PWA概述
譯文出自:我的個人博客
在本文中,您將學習如何使用 Workbox 和 IndexedDB 創(chuàng)建離線優(yōu)先、數據驅動的漸進式Web應用程序(PWA)。在離線的情況下也可以使用后臺同步功能將應用程序與服務器同步。
將會學習到如何使用 Workbox 緩存應用程序
如何使用 IndexedDB 存儲數據
如何在用戶脫機時從 IndexedDB 中檢索和顯示數據
脫機時如何保存數據
如何在脫機時使用后臺同步更新應用程序
應該了解的HTML, CSS, 和 JavaScript
ES2015 Promises
如何使用命令行
熟悉一下 Workbox
熟悉一下 Gulp
熟悉一下 IndexedDB
需具備的條件擁有 terminal/shell 訪問權限的電腦
Chrome 52 或更高版本
編輯器
Nodejs 和 npm
設置如果你沒有安裝 Nodejs 需要安裝一下
之后通過下面的方式 clone 快速啟動倉庫
git clone https://github.com/googlecodelabs/workbox-indexeddb.git
或者直接下載 壓縮包
安裝依賴并啟動服務到下載好的 git 倉庫目錄中,轉到 project 文件夾
cd workbox-indexeddb/project/
然后安裝依賴并啟動服務
npm install npm start說明
這個步驟中會根據 package.json 定義的依賴并安裝,打開 package.json 文件查看,有很多依賴,大部分是開發(fā)環(huán)境需要的(你可以忽略),主要的依賴是:
workbox-sw Workbox
workbox-background-sync 是 Workbox 用來后臺同步的,稍后會提到
gulp 和 workbox-build 是構建工具
npm start 會構建并輸出到 build 文件夾,啟動 dev server,并且會開啟一個 gulp watch 任務。gulp watch 會監(jiān)聽文件的修改自動構建。concurrently 可以同時跑 gulp 和 dev server
打開應用打開 Chrome 并且跳轉到 localhost:8081 你會看到一個事件列表的控制臺,在彈出的權限確認菜單中點擊允許
我們使用通知系統來告知用戶 app 的后臺同步已經更新,試著測試一下頁面底部的添加功能
說明這個小項目的目標是離線保存用戶的事件日歷。你可以查看一下 app/js/main.js 文件的 loadContentNetworkFirst 方法當前是怎么工作的,首先會請求 server,成功則更新頁面,失敗會在控制臺打印一個信息,目前脫機是無法使用的,接下來我們添加一些方法使它脫機可用。
緩存 app shell 編寫 service worker要想脫機工作,就需要 server worker,現在寫一個。
把下面的代碼添加到 app/src/sw.js
importScripts("workbox-sw.dev.v2.0.0.js"); importScripts("workbox-background-sync.dev.v2.0.0.js"); const workboxSW = new WorkboxSW(); workboxSW.precache([]);說明
在開頭我們引入了 workbox-sw 和 workbox-background-sync
workbox-sw 包含了 precache 和向 service worker 添加路由的方法
workbox-background-sync 是在 service worker 中后臺同步的庫,稍后會提到
precache 方法接收一個文件列表的數組,先用一個空的,下一步我們會用 workbox-build 去計算出這個數組的結果。
構建 service worker推薦使用 Workbox 的構建模塊,比如 workbox-build
把下面的代碼添加進 project/gulpfile.js
gulp.task("build-sw", () => { return wbBuild.injectManifest({ swSrc: "app/src/sw.js", swDest: "build/service-worker.js", globDirectory: "build", staticFileGlobs: [ "style/main.css", "index.html", "js/idb-promised.js", "js/main.js", "images/**/*.*", "manifest.json" ], templatedUrls: { "/": ["index.html"] } }).catch((err) => { console.log("[ERROR] This happened: " + err); }); });
現在取消一些注釋:
gulpfile.js:
// uncomment the line below: const wbBuild = require("workbox-build"); // ... gulp.task("default", ["clean"], cb => { runSequence( "copy", // uncomment the line below: "build-sw", cb ); });
保存修改,因為修改了 gulp,我們得重新跑一下,Ctrl + C 退出當前的進程,重新運行 npm start,會看到 service worker 的文件被生成在了 build/service-worker.js
取消 app/index.html 中 service worker 注冊代碼的注釋
if ("serviceWorker" in navigator) { navigator.serviceWorker.register("service-worker.js") .then(function(registration) { console.log("Service Worker registration successful with scope: ", registration.scope); }) .catch(function(err) { console.log("Service Worker registration failed: ", err); }); }
保存修改,刷新瀏覽器 service worker 就會被安裝。Ctrl + C 關閉 dev server,再返回到瀏覽器中刷新頁面,已經可以脫機運行了!
說明在這一步中,workbox-build 和 build-sw 任務被合并到我們的 gulp 文件中,我們的構建過程是使用 workbox-build 庫來從 swSrc(app/src/sw.js) 中生成 service work 到 swDest(build/service-worker.js),來自 globDirectory(build) 的 staticFileGlobs 文件被注入到 build/service-worker.js 以供 precache 調用,還有每個文件的修訂哈希。templatedUrls 選項告訴 Workbox 我們的站點以 index.html 的內容響應請求。
順便貼一個 injectManifest 的鏈接
安裝生成好的 service worker 緩存 app shell 的資源文件,Workbox 會自動去:
為緩存資源設置緩存優(yōu)先策略,允許應用程序離線加載
service work 更新時,使用修訂哈希來更新緩存的文件
創(chuàng)建 IndexedDB 數據庫目前為止還不能離線加載數據,我們接下來創(chuàng)建一個 IndexDB 來保存程序的數據,數據庫命名為 dashboardr
添加下面代碼到 app/js/main.js
function createIndexedDB() { if (!("indexedDB" in window)) {return null;} return idb.open("dashboardr", 1, function(upgradeDb) { if (!upgradeDb.objectStoreNames.contains("events")) { const eventsOS = upgradeDb.createObjectStore("events", {keyPath: "id"}); } }) }
取消調用 createIndexedDB 的注釋:
const dbPromise = createIndexedDB();
保存文件,重啟 server:
npm start
回到瀏覽器刷新頁面,激活 skipWaiting 并再次刷新頁面,在 Chrome 中,你可以在開發(fā)者工具中的 Application 面板中選擇 Service Workers 點擊 skipWaiting,之后使用 開發(fā)者工具 檢查數據庫是否存在。在 Chrome 中你可以在 Application 面板中點擊 IndexedDB 選擇 dashboardr 查看 events 對象是否存在。
注意:開發(fā)者工具的 IndexedDB UI 可能不會準確的反應你數據庫的情況,在 Chrome 中你可以刷新數據庫查看,或者重新打開開發(fā)者工具說明
在上面的代碼中,我們創(chuàng)建了一個 dashboardr 數據庫,并把他的版本號設置為 1 ,然后檢查 events 對象是否存在,這個檢查是為了避免潛在的錯誤,我們還給 event 提供了一個唯一的 key path id。
由于我們修改了 app/main.js 文件,gulp 的 watch 任務會自動構建,Workbox 會自動更新修訂哈希,然后智能更新緩存中的 main.js。
保存數據到 IndexedDB 中現在我們保存數據到剛創(chuàng)建的數據庫 dashboardr 中的 event 對象中。
function saveEventDataLocally(events) { if (!("indexedDB" in window)) {return null;} return dbPromise.then(db => { const tx = db.transaction("events", "readwrite"); const store = tx.objectStore("events"); return Promise.all(events.map(event => store.put(event))) .catch(() => { tx.abort(); throw Error("Events were not added to the store"); }); }); }
然后更新 loadContentNetworkFirst 方法,現在這是完整的方法:
function loadContentNetworkFirst() { getServerData() .then(dataFromNetwork => { updateUI(dataFromNetwork); saveEventDataLocally(dataFromNetwork) .then(() => { setLastUpdated(new Date()); messageDataSaved(); }).catch(err => { messageSaveError(); console.warn(err); }); }).catch(err => { // if we can"t connect to the server... console.log("Network requests have failed, this is expected if offline"); }); }
取消注釋 addAndPostEvent 中的 saveEventDataLocally 調用
function addAndPostEvent() { // ... saveEventDataLocally([data]); // ... }
保存文件,刷新頁面重新激活 service worker。再次刷新頁面,檢查一下來自網絡的數據是否被保存到 events 中去(你可能需要刷新一下開發(fā)者工具中的 IndexedDB)
說明saveEventDataLocally 接收一個數組并一條條的保存到 IndexedDB 數據庫中,我們把 store.put 寫在了 Promise.all 中,這樣如果某一條更新出錯我們就可以終止事務。
loadContentNetworkFirst 方法中,一旦收到來自服務器的數據,就會更新 IndexedDB 和頁面。然后,數據成功保存時,將存儲時間戳,并通知用戶數據可供離線使用。
在addAndPostEvent 中調用 saveEventDataLocally 方法保證了添加新的 event 時本地會存有最新的數據。
從 IndexedDB 中獲取數據離線的時候,我們就要查詢本地緩存的數據。
添加下面的代碼到 app/js/main.js 中:
function getLocalEventData() { if (!("indexedDB" in window)) {return null;} return dbPromise.then(db => { const tx = db.transaction("events", "readonly"); const store = tx.objectStore("events"); return store.getAll(); }); }
然后更新 loadContentNetworkFirst 方法,完整的方法如下:
function loadContentNetworkFirst() { getServerData() .then(dataFromNetwork => { updateUI(dataFromNetwork); saveEventDataLocally(dataFromNetwork) .then(() => { setLastUpdated(new Date()); messageDataSaved(); }).catch(err => { messageSaveError(); console.warn(err); }); }).catch(err => { console.log("Network requests have failed, this is expected if offline"); getLocalEventData() .then(offlineData => { if (!offlineData.length) { messageNoData(); } else { messageOffline(); updateUI(offlineData); } }); }); }
保存文件,刷新瀏覽器激活更新的 service worker,現在 Ctrl + C 關閉 dev server,返回到瀏覽器中刷新頁面,現在 app 和數據都可以離線加載了!
說明loadContentNetworkFirst 被調用的時候如果沒有網絡連接,getServerData 會被 reject,之后便會進入到 catch 中去,然后 getLocalEventData 會調用本地緩存的數據。有網絡連接的話會正常的請求 server 并且 updateUI
使用 workbox-background-sync我們的 app 已經可以離線保存和瀏覽數據,現在我們來用 workbox-background-sync 把離線狀態(tài)下保存的數據同步到服務端去。
把下面的的代碼添加到 app/src/sw.js
let bgQueue = new workbox.backgroundSync.QueuePlugin({ callbacks: { replayDidSucceed: async(hash, res) => { self.registration.showNotification("Background sync demo", { body: "Events have been updated!" }); } } }); workboxSW.router.registerRoute("/api/add", workboxSW.strategies.networkOnly({plugins: [bgQueue]}), "POST" );
保存,現在轉到命令行:
npm run start
刷新瀏覽器,激活更新的 service worker
Ctrl + C 把 app 變?yōu)殡x線狀態(tài),添加一個 event 確認請求 /api/add 已經被添加進 bgQueueSyncDB 的 QueueStore 對象。
說明當用戶試圖在離線情況下添加 event 的時候,workbox-background-sync 會把失敗的請求保存為一個離線隊列,當用戶重新聯網 backgroundSync 會重新發(fā)送這些請求,甚至都不需要用戶打開 app!但是,從聯網到重新發(fā)請求的這個過程大概需要 5 分鐘,下一節(jié)我們將會介紹如何在 app 中立即發(fā)送這些請求。
重發(fā)請求因為重發(fā)請求會有延遲,所以用戶可能回到 app 之后還沒有同步數據,所以我們在用戶聯網的時候立即發(fā)送這些請求。
把下面的代碼添加到 app/src/sw.js
workboxSW.router.registerRoute("/api/getAll", () => { return bgQueue.replayRequests().then(() => { return fetch("/api/getAll"); }).catch(err => { return err; }); });
只要用戶請求服務端數據(加載或刷新頁面時),該路由就會 replay 排隊的請求,然后返回最新的服務端數據。這很好,但是用戶還是得刷新頁面去重新獲取數據,我們還有更好的做法。
把下面的代碼添加進 app/js/main.js
window.addEventListener("online", () => { container.innerHTML = ""; loadContentNetworkFirst(); });
重啟 server
npm start
刷新瀏覽器激活新的 service worker,并再次刷新頁面。
Ctrl + C 把 app 變?yōu)殡x線狀態(tài)
添加一條 event
重啟 server
npm start
這時你應該能立即收到一條數據更新的通知,檢查 server-data/events.json 中的數據是否已經更新。
說明頁面加載的時候會請求 /api/getAll,我們攔截了這個請求,之后主要做了兩件事:
同步本地的離線數據
重新請求 /api/getAll
也就是在重新獲取服務端的數據之前先同步
注意:本例中的網絡請求設計的非常簡單,實際情況下你可能需要考慮更多因素去減少請求的數量。添加刪除功能
下面的時間就交給你了,添加一個刪除的功能,記得刪除 IndexedDB 中的數據。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107129.html
摘要:我喜歡移動,而且也是那些堅持使用技術構建移動應用程序的人之一。我們準備做這樣的一個漸進式應用是典型的旨在提高用戶離線體驗的應用。當我們開始構建應用時,你就能理解上面的場景了。的作用范圍是針對相對路徑的。最佳的做法是在應用的入口。 我喜歡移動app,而且也是那些堅持使用Web技術構建移動應用程序的人之一。 經過技術的不斷迭代(可能還有一些其它的東西),移動體驗設計愈來愈平易近人,給予用戶...
摘要:基本上是使用現代技術構建的網站但是體驗上卻像一個移動,在年,谷歌工程師和創(chuàng)造了。此后谷歌就一直致力于讓能給用戶像原生一般的體驗。檢查谷歌瀏覽器的和現在重載你的并且打開,到選項去查看面板,確保這個選項是勾選的。 Web開發(fā)多年來有了顯著的發(fā)展。它允許開發(fā)人員部署網站或Web應用程序并在數分鐘內為全球數百萬人服務。只需一個瀏覽器,用戶可以輸入URL就可以訪問Web應用程序了。隨著 Prog...
摘要:基本上是使用現代技術構建的網站但是體驗上卻像一個移動,在年,谷歌工程師和創(chuàng)造了。此后谷歌就一直致力于讓能給用戶像原生一般的體驗。檢查谷歌瀏覽器的和現在重載你的并且打開,到選項去查看面板,確保這個選項是勾選的。 Web開發(fā)多年來有了顯著的發(fā)展。它允許開發(fā)人員部署網站或Web應用程序并在數分鐘內為全球數百萬人服務。只需一個瀏覽器,用戶可以輸入URL就可以訪問Web應用程序了。隨著 Prog...
摘要:基本上是使用現代技術構建的網站但是體驗上卻像一個移動,在年,谷歌工程師和創(chuàng)造了。此后谷歌就一直致力于讓能給用戶像原生一般的體驗。檢查谷歌瀏覽器的和現在重載你的并且打開,到選項去查看面板,確保這個選項是勾選的。 Web開發(fā)多年來有了顯著的發(fā)展。它允許開發(fā)人員部署網站或Web應用程序并在數分鐘內為全球數百萬人服務。只需一個瀏覽器,用戶可以輸入URL就可以訪問Web應用程序了。隨著 Prog...
摘要:漸進式應用入門教程上在這一節(jié)中,我們將介紹的原理是什么,它是如何開始工作的。第一步使用漸進式應用程序需要使用連接。優(yōu)先旋轉方向,可選的值有顯示方式無,和原生應用一樣,最小的一套控件集或者最古老的使用瀏覽器標簽顯示一個包含所有圖片的數組。 上篇文章我們對漸進式Web應用(PWA)做了一些基本的介紹。 漸進式Web應用(PWA)入門教程(上) 在這一節(jié)中,我們將介紹PWA的原理是什么,它是...
閱讀 3757·2021-11-24 09:39
閱讀 2953·2021-11-16 11:49
閱讀 2076·2019-08-30 13:54
閱讀 1104·2019-08-30 13:03
閱讀 1090·2019-08-30 11:10
閱讀 718·2019-08-29 17:10
閱讀 1248·2019-08-29 15:04
閱讀 1211·2019-08-29 13:02