1. 程式人生 > >ASP.NET Core Blazor Webassembly 之 漸進式應用(PWA)

ASP.NET Core Blazor Webassembly 之 漸進式應用(PWA)

Blazor支援漸進式應用開發也就是PWA。使用PWA模式可以使得web應用有原生應用般的體驗。 ## 什麼是PWA PWA應用是指那些使用指定技術和標準模式來開發的web應用,這將同時賦予它們web應用和原生應用的特性。 例如,web應用更加易於發現——相比於安裝應用,訪問一個網站顯然更加容易和迅速,並且你可以通過一個連結來分享web應用。 在另一方面,原生應用與作業系統可以更加完美的整合,也因此為使用者提供了無縫的使用者體驗。你可以通過安裝應用使得它在離線的狀態下也可以執行,並且相較於使用瀏覽器訪問,使用者也更喜歡通過點選主頁上的圖示來訪問它們喜愛的應用。 PWA賦予了我們建立同時擁有以上兩種優勢的應用的能力。 這並不是一個新概念——這樣的想法在過去已經在web平臺上通過許多方法出現了多次。漸進式增強和響應式設計已經可以讓我們構建對移動端友好的網站。在多年以前的Firefox OS的生態系統中離線執行和安裝web應用已經成為了可能。 PWAs, 不但如此,更是提供了所有的甚至是更多的特性,來讓web更加優秀。 > [引用自MDN](https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps/Introduction) 說人話就是PWA可以讓你的web程式跟一般應用一樣執行,有桌面圖示,能離線,沒有瀏覽器位址列,一切看起來想個普通的程式/APP。 ## 新建Blazor PWA程式 使用VS新建一個Blazor程式,選擇Webassembly模式,勾選支援PWA。 ![](https://s1.ax1x.com/2020/06/24/NdJT81.md.png) 支援PWA的Blazor程式主要是多了幾個東西: 1. manifest.json 2. service-worker.js ## manifest.json manifest.json是個清單檔案,當程式被安裝到裝置上的時候會讀取裡面的資訊,名稱是什麼,圖示是什麼,什麼語言等等。 ``` { "name": "BlazorPWA", "short_name": "BlazorPWA", "start_url": "./", "display": "standalone", "background_color": "#ffffff", "theme_color": "#03173d", "icons": [ { "src": "icon-512.png", "type": "image/png", "sizes": "512x512" } ] } ``` ### service-worker.js service-worker用來跑一些後臺任務。它跟瀏覽器主程序是隔離的,也就是說跟原來的JavaScript執行時是分開,當然了它不會阻塞頁面。我們可以用它來完成一些功能,比如對所有的fetch/xhr請求進行過濾,哪些請求走快取,哪些不走快取;比如在後臺偷偷給你拉一些資料快取起來。 ``` // Caution! Be sure you understand the caveats before publishing an application with // offline support. See https://aka.ms/blazor-offline-considerations self.importScripts('./service-worker-assets.js'); self.addEventListener('install', event => event.waitUntil(onInstall(event))); self.addEventListener('activate', event => event.waitUntil(onActivate(event))); self.addEventListener('fetch', event => event.respondWith(onFetch(event))); const cacheNamePrefix = 'offline-cache-'; const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`; const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/ ]; const offlineAssetsExclude = [ /^service-worker\.js$/ ]; async function onInstall(event) { console.info('Service worker: Install'); // Fetch and cache all matching items from the assets manifest const assetsRequests = self.assetsManifest.assets .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url))) .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url))) .map(asset => new Request(asset.url, { integrity: asset.hash })); await caches.open(cacheName).then(cache => cache.addAll(assetsRequests)); } async function onActivate(event) { console.info('Service worker: Activate'); // Delete unused caches const cacheKeys = await caches.keys(); await Promise.all(cacheKeys .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName) .map(key => caches.delete(key))); } async function onFetch(event) { let cachedResponse = null; if (event.request.method === 'GET') { // For all navigation requests, try to serve index.html from cache // If you need some URLs to be server-rendered, edit the following check to exclude those URLs const shouldServeIndexHtml = event.request.mode === 'navigate'; const request = shouldServeIndexHtml ? 'index.html' : event.request; const cache = await caches.open(cacheName); cachedResponse = await cache.match(request); } return cachedResponse || fetch(event.request); } ``` 專案裡有2個service-worker.js檔案,一個是開發時候的沒邏輯,還有一個是釋出時候的有一些快取的邏輯。 ### 執行一下 ![](https://s1.ax1x.com/2020/06/24/NdwIe0.png) 如果是PWA程式,在瀏覽器位址列有個+號一樣的圖示,點選可以把程式安裝到本地。 ![](https://s1.ax1x.com/2020/06/24/Nd0v9g.png) 安裝完了會在桌面生成一個圖示,開啟會是一個沒有瀏覽器位址列的介面。 ![](https://s1.ax1x.com/2020/06/24/NdBhV0.png) 這樣一個PWA程式已經可以運行了。 ## 離線執行 如果只是這樣,僅僅是沒有瀏覽器位址列,那PWA也太沒什麼吸引力了。個人覺得PWA最大的魅力就是可以離線執行,在沒有網路的情況下依然可以執行,這樣才像一個原生編寫的程式。 ### 修改service-worker 離線的原理也很簡單,就是請求的資料都快取起來,一般是快取Get請求,比如各種頁面圖片等。 ``` // In development, always fetch from the network and do not enable offline support. // This is because caching would make development more difficult (changes would not // be reflected on the first load after each change). self.addEventListener('fetch', event => event.respondWith(onFetch(event))); self.addEventListener('install', event => event.waitUntil(onInstall(event))); async function onInstall(event) { console.info('Service worker: Install'); } async function onFetch(event) { let cachedResponse = null; const cache = await caches.open('blazor_pwa'); if (event.request.method === 'GET') { const request = event.request; cachedResponse = await caches.match(request); if (cachedResponse) { return cachedResponse; } var resp = await fetch(event.request) cache.put(event.request, resp.clone()); return resp; } return fetch(event.request); } ``` 修改一下sevice-worker.js,把GET請求全部快取起來。這裡為了演示圖方便,其實情況顯然不會這麼簡單粗暴。為了能快取頁面,顯然必須先線上執行成功一次。 ### 模擬離線 當我們修改完上面的js,然後線上正常一次後,可以看到所有GET請求的資源都被快取起來了。 ![](https://s1.ax1x.com/2020/06/24/NdHO9e.png) 我們可以用chrome來模擬離線情況: ![](https://s1.ax1x.com/2020/06/24/Ndb1CF.png) 選擇offline模式,然後重新整理我們的頁面,如果依然可以正常執行則表示可以離線執行。 ![NdOdU0.gif](https://s1.ax1x.com/2020/06/24/NdOdU0.gif) ## 總結 使用Blazor可以快速的開發PWA應用。利用PWA跟Blazor Webassembly的特性,可以開發出類似桌面。或者這是跨平臺桌面應用開發除了electron的又一種方案吧。