なになれ

IT系のことを記録していきます。

PWA BuilderでPWA化してみてPWAを学ぶ

PWA BuilderというPWA化するためのToolを試してみた記録です。
ざっくりとPWAとは何なのかが分かる内容になっていると思います。

今回は以下のサンプルを利用しています。
Freelancer - One Page Theme - Start Bootstrap

PWA化したデモサイトはこちらです。
Freelancer - Start Bootstrap Theme

PWAとは

PWAはProgressive Web Appsのことです。
PWA Builderの説明によると、以下の内容を含んだものがPWAと言えると思います。

  • Web App Manifestを使用してモバイル端末等からインストールおよび起動されたときの見た目や動作を制御している
  • Service Workerを使用してオフライン対応がされている

マニフェストファイルを作成する

PWA Builderの手順に沿って説明します。
まずはGenerate Manifestでの手順です。
PWA Builderを使うと以下のマニフェストファイルが作成されます。

manifest.json

{
    "dir": "ltr",
    "lang": "Japanese",
    "name": "Freelancer - Start Bootstrap Theme",
    "scope": "/",
    "display": "standalone",
    "start_url": "https://arched-photon-204013.firebaseapp.com/",
    "short_name": "Freelancer",
    "theme_color": "transparent",
    "description": "",
    "orientation": "any",
    "background_color": "transparent",
    "related_applications": [],
    "prefer_related_applications": false,
    "icons": [
        {
            "src": "img/d2248ed4-ab6f-84da-fe68-5962b432d4ac.webPlatform.png",
            "sizes": "48x48",
            "type": "image/png"
        },
        {
            "src": "img/b1d7d8f5-1b9b-a7e9-e644-ba9db81e4d93.webPlatform.png",
            "sizes": "1240x600",
            "type": "image/png"
        },
        {
            "src": "img/4f8afd0d-e3e4-1f11-4f22-3bd7ebfb0c61.webPlatform.png",
            "sizes": "300x300",
            "type": "image/png"
        },
        {
            "src": "img/9c2802bf-a937-d809-fd3a-52f1059a43d8.webPlatform.png",
            "sizes": "150x150",
            "type": "image/png"
        },
        {
            "src": "img/fe805efb-f87e-ff12-eb44-86cc76225d06.webPlatform.png",
            "sizes": "88x88",
            "type": "image/png"
        },
        {
            "src": "img/bd3ec8f1-9ef0-de1f-3d55-093be35a1b5b.webPlatform.png",
            "sizes": "24x24",
            "type": "image/png"
        },
        {
            "src": "img/335ca134-e557-a127-dd7a-f1af5ee35285.webPlatform.png",
            "sizes": "50x50",
            "type": "image/png"
        },
        {
            "src": "img/e19531de-b9a8-0cf7-cc34-0d91b68a0339.webPlatform.png",
            "sizes": "620x300",
            "type": "image/png"
        },
        {
            "src": "img/01b4b12b-d6f8-5e7c-897b-642ae94809b2.webPlatform.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "img/a220fe60-f154-a4b0-2645-7d962bdffd78.webPlatform.png",
            "sizes": "144x144",
            "type": "image/png"
        },
        {
            "src": "img/f699c6fb-cd5b-58a0-6e9a-352f1520876c.webPlatform.png",
            "sizes": "96x96",
            "type": "image/png"
        },
        {
            "src": "img/809fd415-acc1-e2b3-39fb-70e2e867e0e9.webPlatform.png",
            "sizes": "72x72",
            "type": "image/png"
        },
        {
            "src": "img/233576f2-1bb0-e4cd-c0a3-bd0e4605f3c9.webPlatform.png",
            "sizes": "36x36",
            "type": "image/png"
        },
        {
            "src": "img/c1007a03-b2e6-e063-b1a6-e809c502ee0d.webPlatform.png",
            "sizes": "1024x1024",
            "type": "image/png"
        },
        {
            "src": "img/c84d6d5e-051e-1555-dca8-c80c1c2a3181.webPlatform.png",
            "sizes": "180x180",
            "type": "image/png"
        },
        {
            "src": "img/df1a52c0-a9dd-8923-51c4-8fd1ab582afb.webPlatform.png",
            "sizes": "152x152",
            "type": "image/png"
        },
        {
            "src": "img/19a904d2-30d1-720c-52b6-c44bd97cf378.webPlatform.png",
            "sizes": "120x120",
            "type": "image/png"
        },
        {
            "src": "img/3235cb0f-c9a5-2a68-d389-9168a8afe050.webPlatform.png",
            "sizes": "76x76",
            "type": "image/png"
        },
        {
            "src": "img/android-launchericon-512-512.png",
            "sizes": "512x512",
            "type": "image/png"            
        }
    ]
}

注目する設定としては、displayプロパティの指定でネイティブアプリを起動するような形でWebアプリを起動することができます。
pwabuilder.comからマニフェストファイルを作成できる対象は公開されているサイトになります。
今回はFirebase上にデプロイしてサイトを公開しています。

Service WorkerのJavaScriptファイルを作成する

Build Service Workerでの手順です。
Cache-first networkを選択します。
以下のファイルが作成されます。

pwabuilder-sw-register.js

//This is the service worker with the Cache-first network

//Add this below content to your HTML page, or add the js file to your page at the very top to register service worker
if (navigator.serviceWorker.controller) {
  console.log('[PWA Builder] active service worker found, no need to register')
} else {

//Register the ServiceWorker
  navigator.serviceWorker.register('pwabuilder-sw.js', {
    scope: './'
  }).then(function(reg) {
    console.log('Service worker has been registered for scope:'+ reg.scope);
  });
}

index.htmlで読み込むJavaScriptファイルになります。
ServiceWorkerを登録するために利用します。

pwabuilder-sw.js

//This is the service worker with the Cache-first network

var CACHE = 'pwabuilder-precache';
var precacheFiles = [
      /* Add an array of files to precache for your app */
      'vendor/bootstrap/css/bootstrap.min.css',
      'vendor/font-awesome/css/font-awesome.min.css',
      'vendor/magnific-popup/magnific-popup.css',
      'css/freelancer.min.css',
      'img/profile.png',
      'img/portfolio/cabin.png',
      'img/portfolio/cake.png',
      'img/portfolio/circus.png',
      'img/portfolio/game.png',
      'img/portfolio/safe.png',
      'img/portfolio/submarine.png',
      'vendor/jquery/jquery.min.js',
      'vendor/bootstrap/js/bootstrap.bundle.min.js',
      'vendor/jquery-easing/jquery.easing.min.js',
      'vendor/magnific-popup/jquery.magnific-popup.min.js',
      'js/jqBootstrapValidation.js',
      'js/contact_me.js',
      'js/freelancer.min.js',
      'manup.min.js'
    ];

//Install stage sets up the cache-array to configure pre-cache content
self.addEventListener('install', function(evt) {
  console.log('The service worker is being installed.');
  evt.waitUntil(precache().then(function() {
    console.log('[ServiceWorker] Skip waiting on install');
      return self.skipWaiting();

  })
  );
});


//allow sw to control of current page
self.addEventListener('activate', function(event) {
console.log('[ServiceWorker] Claiming clients for current page');
      return self.clients.claim();

});

self.addEventListener('fetch', function(evt) {
  console.log('The service worker is serving the asset.'+ evt.request.url);
  if(!evt.request.url.includes('https')){
    return;
  }
  evt.respondWith(fromCache(evt.request).catch(fromServer(evt.request)));
  evt.waitUntil(update(evt.request));
});


function precache() {
  return caches.open(CACHE).then(function (cache) {
    return cache.addAll(precacheFiles);
  });
}


function fromCache(request) {
  //we pull files from the cache first thing so we can show them fast
  return caches.open(CACHE).then(function (cache) {
    return cache.match(request).then(function (matching) {
      return matching || Promise.reject('no-match');
    });
  });
}


function update(request) {
  //this is where we call the server to get the newest version of the 
  //file to use the next time we show view
  return caches.open(CACHE).then(function (cache) {
    return fetch(request).then(function (response) {
      return cache.put(request, response);
    });
  });
}

function fromServer(request){
  //this is the fallback if it is not in the cache to go to the server and get it
return fetch(request).then(function(response){ return response})
}

ServiceWorkerによるキャッシュを行うJavaScriptファイルになります。
precacheFilesという配列にキャッシュを行うファイルを手動で記述する必要があります。

PWAとして構築する

WebでDownloadを選択します。

Web App Manifestの設定

  • manifest.jsonとiconのイメージを配置します。
  • manifest.jsonを読み込みます。
<link rel="manifest" href="manifest.json"></link>
  • Web App Manifestのpolyfillを適用します。
<script src="manup.js"></script>

ファイルの場所
https://github.com/boyofgreen/manUp.js/

Service Workerの設定

  • pwabuilder-sw-register.jsを読み込みます。
<script src="pwabuilder-sw-register.js"></script>

結果

Android端末にインストールしてみました。
オフライン時でも表示ができています。

f:id:hi1280:20180618214555g:plain