Visual WebGUI -> PWA

PWA 係咩?唔知嘅就去讀喱篇文章,再返嚟。

有啲搞笑?將阿爺嗰代嘅 app 轉成 PWA,我都唔係咁信,不過嘅然 Google 己經將佢大部份嘅 apps 都改用 PWA,咁我都要試下如果將我啲 Visual WebGUI 寫嘅 web apps 轉成 PWA 會係點?

Proof-of-concept 我先簡化一個 Visual WebGUI app,將佢做成 PWA:

哪,都睇倒係咪?一個好簡單嘅功能,右上角有下拉 menu,menu 有 login、轉 dark theme、由 Plate 轉去 Film、Film 同 Plate 都有 checkboxes、有 upload 檔案功能,全 API 操作。

隻 GIF 好眼花,我哋去睇下 Google 嘅 Lighthouse 點講:

PWA 要求,幾乎全部達標!仲可以有得 install 添:

通過,用得,可以 deploy。

點整?依家講:

Create 一個 Visual WebGUI app,要係 library:

即係自己唔識 run 嘅,要由人哋 call 佢先有嘢睇嘅 project。你見喇,有個叫 BaseForm 嘅 嘢,配合 Forms. Film、Login、Plate、同 Theme 嚟,做到隻 GIF 入面嘅功能。然後就 create 一隻 asp.net app:

如上圖,啲 images 可以喺網上 gen 出嚟,通常係你上載一隻 512 Pixels 嘅 JPEG,佢就幫你 gen 其他出嚟,仲會俾埋啲正正常常嘅檔案名俾你,方便好用。

個 project 得幾個重要嘅 programs,Default.aspx、manifest.json、同埋 service-worker.js

隻 Web.config 係由個 library project 直接抄過嚟用就 okay,service-worker.js 係:

// Set this to true for production
var doCache = true;

// Name our cache
var CACHE_NAME = 'x5-speedbox';

// Delete old caches that are not our current one!
self.addEventListener("activate", event => {
    const cacheWhitelist = [CACHE_NAME];
    event.waitUntil(
      caches.keys()
        .then(keyList =>
            Promise.all(keyList.map(key => {
                if (!cacheWhitelist.includes(key)) {
                    console.log('Deleting cache: ' + key);
                    return caches.delete(key);
                }
            }))
        )
    );
});

// The first time the user starts up the PWA, 'install' is triggered.
self.addEventListener('install', function (event) {
    if (doCache) {
        event.waitUntil(
          caches.open(CACHE_NAME)
            .then(function (cache) {
                // Get the assets manifest so we can see what our js file is named
                // This is because webpack hashes it
                fetch("asset-manifest.json")
                    .then(response => {
                        response.json(); 
                    })
                    .then(assets => {
                        // Open a cache and cache our files
                        // We want to cache the page and the main.js generated by webpack
                        // We could also cache any static assets like CSS or images
                        const urlsToCache = [
                            '/',
                            //'/Home/About',
                            //'/Home/Index',
                            //'/Home/Contact'
                        ];
                        cache.addAll(urlsToCache);
                        console.log('cached');
                    });
            })
        );
    }
});

// When the webpage goes to fetch files, we intercept that request and serve up the matching files
// if we have them
self.addEventListener('fetch', function (event) {
    if (doCache) {
        // SOLUTION: 
        // i) detect your upload endpoint 
        // ii) return so that you can use XHR
        if (event.request.url.indexOf('BaseForm') !== -1) {
            return;
        }
        event.respondWith(
            caches.match(event.request).then(function (response) {
                return response || fetch(event.request);
            })
        );
    }
});

 

唔係好難明啫,manifest.json 仲簡單:

{
  "short_name": "x5 SpeedBox",
  "name": "x5 SpeedBox PWA",
  "icons": [
    {
      "src": "Resources/Images/pwa-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "Resources/Images/pwa-512x512.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ],
  "start_url": "/",
  "background_color": "#222",
  "theme_color": "#222",
  "display": "standalone"
}

Default.aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="xFilm5.SpeedBox.Web.Default" %>
<%@ Register Assembly="Gizmox.WebGUI.Forms" Namespace="Gizmox.WebGUI.Forms.Hosts" TagPrefix="vwg"%>
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head runat="server">
    <title>xFilm SpeedBox</title>
    <meta name="description" content="xFilm5 SpeedBox PWA" />
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="/favicon.ico" />

    <!-- Suppress the small zoom applied by many smartphones by setting the initial scale and minimum-scale values to 0.86 -->
    <meta name="viewport" content="width=device-width, initial-scale=0.86, maximum-scale=3.0, minimum-scale=0.86" />
    <!-- Add manifest -->
    <link rel="manifest" href="/manifest.json" />
    <!-- Tell the browser it's a PWA -->
    <meta name="mobile-web-app-capable" content="yes" />
    <!-- Tell iOS it's a PWA -->
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="theme-color" content="#536878" />

    <link rel="apple-touch-icon" href="Resources/Images/ios-appicon-180-180.png" />
    <link rel="apple-touch-icon" sizes="152x152" href="Resources/Images/ios-appicon-152-152.png" />
    <link rel="apple-touch-icon" sizes="180x180" href="Resources/Images/ios-appicon-180-180.png" />
    <!-- 
    @*<link rel="apple-touch-icon" sizes="167x167" href="Resources/Images/touch-icon-ipad-retina.png" />*@
    -->
</head>
<body style="margin:0px;">
    <form id="form1" runat="server">
    <div>
        <vwg:FormBox runat="server" Form="BaseForm" Height="400" Width="412" Title="VWG" />
    </div>
    </form>
    <script>
      if ('serviceWorker' in navigator) {
        window.addEventListener('load', function() {
          navigator.serviceWorker.register('service-worker.js').then(function(registration) {
            // Registration was successful
            console.log('ServiceWorker registration successful with scope: ', registration.scope);
          }, function(err) {
            // registration failed :(
            console.log('ServiceWorker registration failed: ', err);
          }).catch(function(err) {
            console.log(err)
          });
        });
      } else {
        console.log('service worker is not supported');
      }
    </script>
    <noscript>
        <p>Your browser does not support JavaScript or it is disabled<br />This websit requires JavaScript to work properly.</p>
    </noscript>
</body>
</html>

 

#2 配合 #32 用,係 call 隻 Visual WebGUI library 入嚟,即係我用 aspx 包住隻 Visual WebGUI library 嘅 BaseForm;#12~#27、#35~#54 係標準答案,應該係人人都啱用。

隻 Default.aspx.cs 冇嘢嘅,不過都睇埋啦:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace xFilm5.SpeedBox.Web
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }
    }
}

就係咁簡單,淨係講,你未必信,做出嚟俾你睇! :mrgreen: