2016年有了一個新的術語:漸進式web應用(PWA) ,一個為響應式設計的「保護傘」式的術語,獨立連接性、新穎、安全、可探索、可重定制、可安裝性、可連結的應用程式/web站點,並帶有原生應用程式般的互動效能。再次,我們都需要學會怎麼去整個PWA這個概念, 我們需要更多的討論和交換意見、並找出什麼能讓我們的網站更加合理 – 讓我們從開始響應式web設計時就不要重複這些錯誤。
什麼是漸進式 Web 應用
漸進式 Web 應用(PWA)本質上與普通的網站沒有什麼不同 - 它也是由 HTML、CSS 和 JavaScript 組成,並且存活於瀏覽器中。將 PWA 與普通網站區分開的是它需要滿足的 10 個核心概念清單。以下就是直接從 Google 開發者網站上摘錄的這個清單:
安全 – 透過 HTTPS 來提供服務來防止網路窺探,確保內容不被竄改。
漸進式 – 能夠讓每位使用者使用,無論使用者使用什麼瀏覽器,因為它始終以漸進式增強為原則
響應式 – 適應任何環境:桌上型電腦、智慧型手機、平板電腦,或其他設備。
不依賴網路連線 – 透過用service workers 增強,可以在離線或低品質網路下運作
類原生應用 – 有像原生應用般的互動和導航給使用者原生應用般的體驗,因為它是建立在app shell model 上的。
持續更新 – 受益於 service worker 的更新進程,應用程式能夠始終保持更新。
可發現 – 可識別為“應用程式”,是得益於 W3C manifests 元資料和 service worker 的登記,讓搜尋引擎能夠找到 web 應用。
可再次存取 – 透過推播通知等特性讓用戶再次存取變得容易。
可安裝 – 允許使用者保留對他們有用的應用在主螢幕上,不需要透過應用程式商店。
可連結 – 透過 URL 可輕鬆分享應用,不用複雜的安裝即可運作。
遵循這些指南,會確保你的應用程式不僅在瀏覽器中查看時,還能在單獨透過主畫面快捷鍵啟動時,都運行的不錯。你可能會發現 Google 選用的措辭令人相當困惑,不過不要著急,我們會在本教程中一個一個解釋這些規則。
漸進式Web 應用不是什麼
PWA 的概念不應該與以下概念混淆:
基於 Cordova 的混合app
React Native
是把HTML 應用程式打包成執行文件,例如 .apk、.exe 等等,然後必須從各自的應用程式商店下載下來,安裝到使用者的裝置上。
PWA 不需要安裝,也依然無法用在 Google Play 或 iTunes 應用程式商店。要下載一個 PWA,只需要訪問它的網站,然後將其作為快捷鍵保存到主畫面。對 iOS 和 Android 各自開發和維護一個版本不再是問題,但需要考慮瀏覽器支援不支援。
1. 安全性
大多數漸進式 Web 應用要使用原生 API 和 service worker 這些處理敏感資料的技術,需要謹慎處理。這就是為什麼每個 PWA 都要透過 HTTPS 連線存取的原因。
如果你沒有帶有 SSL 憑證的伺服器,那麼在安全環境下運行專案的最簡單的方法是透過 GitHub Pages 或類似的服務。所有 Github 庫都可以直接透過 HTTPS 存放,而 GitHub 和 GitHub Pages 對公共倉庫都是免費的。
如果只是在本機伺服器上測試,你還可以試試 Ngrok。它是一個小工具,可以在任何目前運行的本機主機和一個公共 URL 之間建立一個安全通道。 Ngrok 是免費的,並且可用在 Windows、Mac 和 Linux 系統上。
2. 漸進式
本質上,漸進式的意思是,PWA 應該使用被廣泛支援的 Web 技術,並且可以同樣運行在盡可能多的瀏覽器上。眾所周知,在 Web 開發領域,這近乎是不可能的,但是我們仍然可以做一些事情來覆蓋更大的用戶群。
例如,在 PhotoBooth 應用程式中,我們使用 getUserMedia() API 存取一個裝置上的硬體相機。它在不同瀏覽器上的支援很不一致 - Safari 完全不支持,而支援它的瀏覽器需要前綴,並且用法各不相同。
為確保更多的人可以使用我們的應用程序,我們涵蓋了所有前綴:
navigator.getMedia = ( navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia );
如果所有前綴都不行,就顯示一個錯誤:
if (!navigator.getMedia) { displayErrorMessage("你的浏览器不支持 navigator.getUserMedia 接口。"); }else { // 使用 Camera API }
在可能的情況下,應該提供回退(Fallback )和填補(polyfill)。同樣的原則也適用於 CSS 和 HTML 程式碼。
3. 響應式
應用程式應該在所有裝置上都看起來不錯,不管螢幕大小是多少。我們應用的 UI 相當簡單,所以只用了兩個媒體查詢來控製字體大小、padding、margin 等。
不要害怕使用 CSS 函式庫和框架,例如 Bootstrap。它們會讓表單柵格、排版處理和普通的響應性變得容易。
4. 獨立於網路連線
這是很重要的一點。使用 service worker 可以讓應用程式在無網路連線的情況下仍可運作。
有些應用可以只部分快取:UI 被緩存,並且離線可用,動態內容仍然需要存取伺服器。
有些應用,例如我們的 PhotoBooth 示範應用,可以全部快取。所有原始程式碼和資源都將保存在本地,應用程式在離線和線上情況以完全相同的方式運作。如下是讓這種奇蹟得以發生的程式碼:
這是 Service Worker 的過度簡化用法,在商業專案中要慎用。
首先需要建立一個 service worker JavaScript 文件,定義背後的邏輯。
sw.js
// 安装 service worker. this.addEventListener('install', function(event) { event.waitUntil( caches.open('v1').then(function(cache) { // 如果这些资源中有任何资源不能保存,缓存就会失败 return cache.addAll([ // 路径是相对于缓存来源,而不是应用程序的目录。 '/pwa-photobooth/', '/pwa-photobooth/index.html', '/pwa-photobooth/assets/css/styles.css', '/pwa-photobooth/assets/fonts/MaterialIcons-Regular.woff2', '/pwa-photobooth/assets/js/script.js', '/pwa-photobooth/assets/icons/ic-face.png', '/pwa-photobooth/assets/icons/ic-face-large.png', '/pwa-photobooth/manifest.json' ]) .then(function() { console.log('成功! App 离线可用!'); }) }) ); }); // 定义一个资源被请求时候会发生什么 // 对于我们的应用,我们以缓存优先的方式 self.addEventListener('fetch', function(event) { event.respondWith( // 试着从缓存中获取. caches.match(event.request) .then(function(response) { // 如果资源没有存储在缓存中,就回退到网络 return response || fetch(event.request); }) ); });
然後,我們需要將該 service worker 連結到 HTML。
index.html
// 注册 Service Worker. if ('serviceWorker' in navigator) { // 路径是相对于缓存来源,而不是应用程序的目录. navigator.serviceWorker.register('/pwa-photobooth/sw.js') .then(function(reg) { console.log('Registration succeeded. Scope is ' + reg.scope); }) .catch(function(error) { console.error('Registration failed with ' + error); }); }
現在,專案中的所有檔案都將保存在使用者瀏覽器中。所有 JavaScript 變數和物件也應該盡可能保存在 localStorage 或 IndexDB 中。
5. 類別原生引用
在創建 PWA 時,建議遵循一個稱為應用外殼的架構的設計概念。它聽起來很複雜,但是實際上可以總結為:應用被分為兩個主要組件:外殼 和內容。
外殼包含了所有靜態 UI 元素,例如標題、選單、抽屜等。在快取應用程式時,外殼應該總是保存在裝置上,因為我們想讓它始終可用。這樣,當無網路連線的用戶打開應用程式時,就不會看到空白螢幕或一個奔跑的恐龍 - 而是會看到快取了的應用程式介面以及恰當的錯誤訊息。
内容驻留在外壳内。它也可以被缓存,但是没有必要这样做,因为内容通常是动态的,会频繁发生改变,并且每个页面加载时都可能是不同的。
6. 持续更新
一旦被缓存了,PWA 会总是从本地存储加载。不过,如果以任何方式修改了 service workersw.js,那么在下一个页面加载时,新版本就会被下载和安装。
this.addEventListener('install', function(event) { event.waitUntil( caches.open('v1.0.1').then(function(cache) { // ... }) ); });
通过使用 service worker 更新,我们可以重新下载资源,删除旧缓存,或者彻底修改 service worker 逻辑。你可以从这篇 Google 开发者文章中,学到更多有关 SW 更新过程的知识。
7. 可发现
通过给应用程序添加一个 Web Manifest,可以提供有关应用程序的各种信息,并且可以修改应用程序在用户设备上的显示方式。它允许应用程序被带自定义图标的方式保存到主屏幕上,在一个单独的浏览器窗口中启动,以及很多其它很酷的东西。
Web Manifest 是以一个简单 JSON 文件的形式出现:
manifest.json
{ "name": "Progressive Web App: PhotoBooth", "short_name": "PhotoBooth", "description": "Simple Progressive Web App for taking selfies.", "icons": [{ "src": "assets/icons/ic-face.png", "type": "image/png", "sizes": "72x72" }, { "src": "assets/icons/ic-face-large.png", "type": "image/png", "sizes": "144x144 256x256" }], "start_url": "index.html", "display": "standalone", "background_color": "#fff", "theme_color": "#fff", "orientation": "portrait" }
大多数属性是自解释的,所以我们只讲讲较为重要的一些属性。要查看完整的 Web manifest 格式,以及所有可用的字段,请到这里。
Shortname – 这是应用保存到主屏幕上时的名称。
Icons – 不同分辨率的图标数组。
Display – 定义应用打开的方式。我们选择的是独立(standalone),所以启动 PhoneBooth 时,会以全屏窗口出现,没有浏览器导航栏或者菜单。它还会被看作为多任务中的一个单独的应用。
要注册 Manifest 文件,必须把它链接到 HTML 中:
<!-- Web Manifest --> <link rel="manifest" href="manifest.json">
Safari 还不支持 Web Manifest 标准,但是我们可以用如下的苹果特定的 meta 标记,定义类原生应用的行为:
<!-- Meta tag for app-like behaviour in iOS --> <meta name=”apple-mobile-web-app-capable” content=”yes”>
8. 可再次访问
通知推送不再只限于原生应用。多亏了 service worker 和 Push API,Web 应用程序也可以发送消息给 Android 通知栏。并不是所有应用都可以从这个功能受益,但是当正确使用此功能时,通知确实可以帮助吸引用户。
这个主题已经超出了本教程的范围,因为推送很复杂,值得用一个完整的课程讲解。如果你依然想在你的 Web 应用中实现通知推送,这里有一些最好的学习资源:
Google 开发者网站上的《推送通知:及时、相关和准确》 – 这里.
Google 开发者网站上的《开放 Web 上的推送通知 – 这里.
MDN 上的《使用 Push API》 – 这里.
Push.js 库,提供处理推送通知的更清洁的 API – 这里.
9. 可安装
默认情况下,任何网站都可以用 Chrome 浏览器菜单的 "添加到主屏幕" 按钮,保存到主屏幕上。不过,让用户以这种方式 "安装" 应用程序可能有点难,因为大多数人完全不知道这个功能。
值得慶幸的是,還是有一種方式讓應用程式提示使用者保存它,即採用一個簡單安裝彈出視窗來提示。為防止開發者濫用這些彈出窗口,不允許以程式設計的方式顯示它們。而是在應用程式滿足以下幾個條件時,才會讓這些視窗自行出現:
有一個有效的 Web Manifest。
安裝有有效的 Service Worker。
透過 HTTPS 存取應用程式。
我們滿足上面涉及的所有條件,所以當用戶訪問網站幾次後,會得到這個提示:
10. 可連結
任何人只有要瀏覽器,就可以訪問PWA 應用,這樣應用僅透過其URL 就可以被共享。發現或安裝這些應用程式不需要第三方工具。
如果應用程式以獨立模式運行,因為瀏覽器網址列和選單都是不可見的,所以在應用程式內添加一個共享按鈕也是明智的。
最後
超越響應式
在我第一次體驗響應式設計之後,我意識到給特殊設備做設計和編程是一條不歸路。沒有哪個可產品化的網站是我第一次嘗試就能搞定的, 但其它非特殊設備就可以。今天,大多數人在行動裝置上發現樣式設計的突發狀況會先考慮新增媒體查詢,這很好。
如果我現在考慮響應式設計,那意味著要做比媒體查詢和靈活排版內容更多的事。當做響應式網站時我意識到需要適配性、效能及內容的人越來越多。我意識到如果你忘記做一個響應式網站的適配或效能是沒有用的。我更傾向於一個快速可能如的站點,而不是每次打開一個不可進入的、為了適配螢幕大小而設計的過度加載的響應式設計網站。
同是為了Service Worker,當我第一次想到它僅是一個關於離線的方案,我意識到許多性能方面和使網站更易進入和更好使用的方法。
做漸進式網站– 不僅僅是命名上的
一個漸進式web應用– 響應式、獨立連接性、新穎、安全、可探索、可重定制、可安裝、可鏈接的應用(基本的web站點)帶著出色的猶如原生應用般的交互,但這裡有一個詞我們永遠不能忘記:漸進式。
我明白很難推廣漸進式web的升級優化,所以我想命名他們為漸進式web應用是極好的。我希望有更多的人去做漸進式web站點。使用者會說「你能幫助我們的站點漸進式化嗎?」 那漸進式優化升級就不僅是一陣的事,所有人都會想去那麼做。
漸進式web應用是一個機會
我們剛開始仍然會去找那些實現漸進式web應用最好的解決方法。 我希望那會讓更多的人有漸進式升級優化的意識。我希望人們不會在專注在設備上時犯下同樣的錯誤。響應式設計改變了我建立網站的方式,現在我會更多的去思考web內容、可訪問性、效能及用戶。我希望在建立PWA(漸進式web應用)時能讓更多的人意識到web的基本要素。
我們無須重蹈覆轍。一個PWA僅工作在特定設備上是錯誤的。讓我們建構一個PWA時把重心放在「漸進」的部分,而不是「應用」本身。