ลองทำ PWA ด้วย Vanilla JavaScript


วิธีการทำ Progressive Web Application ด้วย Vanila JavaScript อย่างง่าย

โครงสร้างเบื้องต้นภายใน folder

-|public
   |- index.html
   |- manifest.json
   |- service-worker.js
   |- icon.png
-|index.js
  • Manifest.json
{
    "name": "Thnovice",
    "short_name": "Thnovice",
    "theme_color": "#222831",
    "background_color": "#ffad17",
    "display": "standalone",
    "Scope": "",
    "start_url": "/index.html",
    "icons": [
        { 
            "src" : "/icon.png", 
            "sizes": "196x196",
            "type": "image/png"
        }
    ]
}

Service Worker

มีสามส่วน

  • 1. ส่วน Install เป็นการให้ service เก็บ resource ของเราไว้ใน cache
const cacheName = 'Thnovice-cache';

self.addEventListener('install', e => {
  e.waitUntil(
    caches.open(cacheName).then(cache => {
      return cache.addAll([
        './',
        './index.html',
        './manifest.json'
      ]);
    })
  );
});
  • 2. ส่วนการ add to home screen
let deferredPrompt;
self.addEventListener('beforeinstallprompt', (e) => {
    e.preventDefault();
    deferredPrompt = e;
    addBtn.style.display = 'block';
    addBtn.addEventListener('click', (e) => {
      addBtn.style.display = 'none';
      deferredPrompt.prompt();
      deferredPrompt.userChoice.then((choiceResult) => {
          if (choiceResult.outcome === 'accepted') {
            console.log('User accepted the A2HS prompt');
          } else {
            console.log('User dismissed the A2HS prompt');
          }
          deferredPrompt = null;
        });
    });
});
  • 3. ส่วนที่ไปเรียกใช้ cache
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.open(cacheName)
      .then(cache => cache.match(event.request, { ignoreSearch: true }))
      .then(response => {
        return response || fetch(event.request);
      })
  );
});
  • ทั้งหมด service-worker.js
const cacheName = 'Thnovice-cache';

self.addEventListener('install', e => {
  e.waitUntil(
    caches.open(cacheName).then(cache => {
      return cache.addAll([
        './',
        './index.html',
        './manifest.json'
      ]);
    })
  );
});

let deferredPrompt;
self.addEventListener('beforeinstallprompt', (e) => {
    e.preventDefault();
    deferredPrompt = e;
    addBtn.style.display = 'block';
    addBtn.addEventListener('click', (e) => {
      addBtn.style.display = 'none';
      deferredPrompt.prompt();
      deferredPrompt.userChoice.then((choiceResult) => {
          if (choiceResult.outcome === 'accepted') {
            console.log('User accepted the A2HS prompt');
          } else {
            console.log('User dismissed the A2HS prompt');
          }
          deferredPrompt = null;
        });
    });
});

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.open(cacheName)
      .then(cache => cache.match(event.request, { ignoreSearch: true }))
      .then(response => {
        return response || fetch(event.request);
      })
  );
});

  • index.html
  ....
  <link rel='manifest' href='./manifest.json'>
  <script>
      if('serviceWorker' in navigator) {
            navigator.serviceWorker.register('./service-worker.js', { scope: './' })
      }
  </script>
    ....
    ....
    ....
  • ทั้งหมด ใน index.html
<!DOCTYPE html>
<head>
  <meta name='viewport' content='width=device-width, initial-scale=1'>
  <meta name="theme-color" content="#222831">
  
  <link rel='manifest' href='./manifest.json'>
    <script>
        if('serviceWorker' in navigator) {
            navigator.serviceWorker.register('./service-worker.js', { scope: './' })
        }
    </script>
</head>
<body>
  <div>
    <h1>Hello PWA</h1>
  </div>
</body>

จากนั้นสร้างตัว server

$ yarn init
$ yarn add express body-parser
$ touch index.js
  • ใน index.js
const bodyParser = require('body-parser');
const express = require('express');
const app = express();
const port = 3000;

app.use(bodyParser.json());

app.use(express.static('public'));

app.get('/public');

app.listen(port, () => console.log(`Listening on port ${port}!`));

Start Service

$ node index.js

  • สำหรับทำ icon

Maskable.app Editor