實驗室最近多了一個實驗産品MAX:群控手機的項目。主要包括:後端服務、web前端用戶端、安卓app用戶端。涉及到的程式設計語言:Java、Nodejs。技術上主要涉及安卓的MediaProjection API、配合websocket來實作。
MediaProjection 提供了錄屏功能;websocket主要是傳輸友善,可以做到實時。

今天主要介紹下web前端用戶端的實作,主要使用了PWA技術。
- 什麼是PWA?
PWA全稱Progressive Web Apps,漸進增強 Web 應用程式,它可以離線運作,并且可以在運作的系統中選擇性安裝,它從外觀還是執行效果來看,與一般應用程式無異。
不知大家體驗過微軟的郵件服務沒?Outlook.com已經完成了PWA版本,可以在浏覽器裡面像本地應用一樣直接打開即用。
比如我使用的mac,添加了一個PWA應用之後,底部菜單欄多了一個應用的icon,效果如下:
就是mixlab那個logo
PWA是一系列技術的集合,裡面最核心的是一個叫“app shell”的概念。
- 什麼是App shell?
我們先了解下,渲染網站主要有兩種方法:在伺服器上或在用戶端上。
-伺服器端渲染(SSR)
意味着網站每次都是在伺服器上渲染,是以它提供了更快的首次加載,但是在頁面之間跳轉需要每次都下載下傳所有内容,因而它的加載速度往往會比較慢。
-用戶端渲染(CSR)
頁面是在用戶端(浏覽器)渲染的,因而加載速度往往取決于浏覽器的性能,通路速度會比較快,但是在開始時需要更多的初始下載下傳(首次通路時網站速度較慢),以保證整個網站其他頁面實作用戶端渲染所需要的資料。
兩種方式各有利弊,而PWA使用的方法是“app shell”,它混合了SSR和CSR的方式。
App shell,可以了解為程式的外殼。App shell意圖盡快加載最小的使用者界面,然後緩存它,以便在後續通路時可以離線使用,然後加載應用程式的所有内容。這樣,下次有人從裝置通路應用程式時,UI立即從緩存加載,并從伺服器請求新内容(如果它已在緩存中不可用)。
一個App shell的代碼結構如下:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<title>Max</title>
<meta name="description" content="app的介紹">
<meta name="author" content="作者">
<meta name="theme-color" content="#B12A34">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta property="og:image" content="icons/icon-512.png">
<link rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet" href="style.css">
<link rel="manifest" href="manifest.json">
<script src="app.js" defer></script>
</head>
<body>
<header>
<p>MAX-demo</p>
</header>
<main>
<h1>HELLO WORLD</h1>
<button id="notifications">Request notifications</button>
<section id="content">
// Content inserted in here
</section>
</main>
<footer>
<p>© max 2012-2018, created and maintained by shadow.</p>
</footer>
</body>
</html>
複制
複制
為了配合app shell,需要一個叫Service Worker API的支援。
- Service Worker
Service Worker API可以完成2種任務,一種是緩存App shell所需的資料,另一種是如果你有比較耗時的計算,你可以把它們從主線程中抽離出來,在Service Worker中進行計算,最後在它們計算完畢的時候從Service Worker中取得計算結果。
Service Worker主要由3項技術構成:
- 緩存機制是依賴 Cache API 實作的
- 依賴 HTML5 fetch API 發起網絡請求
- 依賴 Promise 實作異步
service worker是需要注冊的,我們在app.js中,輸入:
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/serviceWorker.js', { scope: '/' })
.then(function(registration) {
// 注冊成功
console.log('ServiceWorker registration successful with scope: ', registration.scope);
})
.catch(function(err) {
// 注冊失敗
console.log('ServiceWorker registration failed: ', err);
});
});
};
複制
即可,使用上我們編寫好的serviceWorker.js檔案。
serviceWorker.js主要對有install跟fetch事件進行監聽,對cache進行操作,達到緩存的目的。
let cacheName = 'max-v1';
let contentToCache = [
'/',
'/index.html',
'/style.css',
'/app.js'
];
// 對app shell和主體内容(content)裡面的資料建立緩存
self.addEventListener('install', function(e) {
console.log('[Service Worker] Install');
e.waitUntil(
// 安裝成功後操作 CacheStorage 緩存,使用之前需要先通過 caches.open() 打開對應緩存空間。
caches.open(cacheName).then(function(cache) {
console.log('[Service Worker] Caching all: app shell and content');
// 通過 cache 緩存對象的 addAll 方法添加 緩存
return cache.addAll(contentToCache);
})
);
});
//如果條件允許,service worker将從緩存中請求content中所需的資料,進而提供離線應用功能
self.addEventListener('fetch', function(e) {
e.respondWith(
caches.match(e.request).then(function(r) {
console.log('[Service Worker] Fetching resource: ' + e.request.url);
return r || fetch(e.request).then(function(response) {
return caches.open(cacheName).then(function(cache) {
console.log('[Service Worker] Caching new resource: ' + e.request.url);
cache.put(e.request, response.clone());
return response;
});
});
})
);
});
複制
兩種方式可以比較一下:
- install
優點是第二次通路即可離線,缺點是需要将需要緩存的 URL 在編譯時插入到腳本中,增加代碼量和降低可維護性;
- fetch
優點是無需更改編譯過程,也不會産生額外的流量,缺點是需要通路過一次才能離線使用。
是以,在設計技術架構的時候,需要考慮到2種方式的優缺點。
除了配置serviceWorker.js之外,我們還需要配置manifest.json檔案。
- 添加至桌面功能
serviceWorker使得網頁在速度跟體驗上接近原生app,除此之外,還需要引導使用者添加pwa應用到桌面,以友善下次使用。
實作 PWA 應用添加至桌面的功能,除了要求站點支援 HTTPS 之外,需要準備 manifest.json 檔案去配置應用的圖示、名稱等資訊。一個基本的 manifest.json 應包含如下資訊:
{
"name": "max-demo-v1",
"short_name": "max",
"description": "demo",
"icons": [{
"src": "icons/mix-logo.png",
"sizes": "72x72 96x96 128x128 256x256",
"type": "image/png"
}],
"start_url": "/index.html",
"display": "fullscreen",
"theme_color": "#4a4a4a",
"background_color": "#eeeeee"
}
複制
運作之後,在浏覽器位址欄右側,可以看到一個+号,點選安裝。
另外,調試可以在chrome的Application面闆中進行檢視。
由于PWA的api不是所有浏覽器都支援,因而,你還需要注意使用caniuse.com 來檢視主流浏覽器的支援情況。
- 如何告知普通使用者什麼是離線模式?或者什麼是PWA?
這是體驗設計上需要注意的地方,我們應該認識到并不是每個使用者都是技術出身,都對PWA的概念了解得很清楚。因而,使用者體驗設計需要為使用者提供指導,以便他們可以了解什麼是PWA(或者離線模式)。
确實,離線模式比PWA(漸進式)更為容易了解,但是離線模式對每個人來說都是一個全新的心智模式。通俗來講,您需要告訴使用者當他們無法連接配接上網絡時會發生什麼變化。比如:
哪些功能無法使用;
或者是頁面上的資料是什麼時間更新的;
目前的網絡連接配接情況;
等等。
除此之外,設計上要考慮首次加載的問題,如首次加載時間過長,需要設計動畫提示,可以把加載的檔案内容簡要告知使用者,讓使用者知道網頁正在加載,而不是“當機了”。
下一步,拟內建TensorFlowJS,探索下可能性。