この記事はPWA Advent Calendar 2020の11日目の記事です。
不動産スタートアップ 株式会社estieでデータエンジニアやっています、🦊 marushoです。
🍳 はじめに
業務と関係のない技術を触ってみたくなることありませんか? ありますよね〜
この記事は「データエンジニアの僕が初めてPWAを触ってみた」ときの備忘録となります。
🐥 きっかけ
昨年、なにか面白そうな本ないかな〜と技術書典7に出向いたときに
みんなで叩いてみた PWAへの入門の扉 という書籍に出会いました。
実は以前、サイゼリヤ1000円ガチャというジョークWebサイトを作ったところ
「PWAにはしないのですか?」と質問を頂いたことがあります。
「PWAって何...?」という状態でした。
上記の本では、同じように「PWAって何?」と思っていたエンジニアの方々がPWAに入門してみた本で
「PWAってどういうもの?」というところから、実装まで優しく解説してくれています。
これを読んでPWAにするぞ〜と思ってから、1年の積読期間を経て
サイゼリヤ1000円ガチャをPWAにしてみようと思います!
PWAへの入門の扉、叩けるだろうか...
📖 PWAって何?
MDNにはこう書かれていました
プログレッシブウェブアプリ(PWA) は、新しいウェブ API と伝統的なプログレッシブな拡張戦略を使用して、クロスプラットフォームのウェブアプリケーションにネイティブアプリと同様の使い勝手をもたらすウェブアプリのことです。
実際に触ってみるのがイメージできるかと思います。
日本経済新聞のサイトをホーム画面に追加してからアクセスしてみると
あら不思議、アプリのようなUIになります。
Webサイトでありながら、ネイティブアプリのような挙動を再現でき
キャッシュによって高速化&オフライン動作&プッシュ通知を実現しています。
なかなか面白い技術ですね〜
🍝 できたもの
いきなりアウトプットですが
サイゼリヤ1000円ガチャが、PWAになりました 🎉
https://saizeriya-1000yen.marusho.io/
見た目はそのままですが、設定を開いて...
ホーム画面に追加して...
アイコンをタップすると...
全画面に表示されてアプリのようなUIになります👌
PCにも追加することができます
ブラウザの右上にある + を押すと、
デスクトップアプリのように動作します
2020/12/09 のグランドメニューにも対応したので、ぜひ遊んでみてください🍗
⛰ PWAにするまで
最後にGithubでコードを公開してます!
🔥 キャッシュで動作するように書き換える
PWAのメリットを活かしてキャッシュを使ってオフラインで動作するようにしたいので
sqliteで持っていたメニューデータをJSON化/フロントのみで動作するように書き換えました。
サイゼリヤのメニューは選び抜かれているので、全て合わせても130種類ほどです。
JSONで20kB程度で、このデータをキャッシュさせることでオフラインでも動作するようにします。
参考
元々メニューデータはsqLiteのdbファイルとして持っていたため、JSONも同じように扱いたいなーと思い
JSONをSQLで扱えるAlaSQLというライブラリを使用しました。
MENU
にJSON入れることで以下のようにDBにクエリを投げる感覚でデータを取得できます。
var sql = 'SELECT * FROM ? d WHERE d.price <= ?';
var candidates = alasql(sql, [MENU, budget]); //メニューから特定の値段以下の商品のみを抽出
👝 PWAに必要なファイルを用意する
PWAにするには4つのポイントがあるようです。
- HTTPSに対応している
-
service-worker.js
を用意する -
manifest.json
を用意する - iOSで全画面対応にするには
<meta>
の設定をする
1.HTTPSに対応している
PWAとして認識される条件にHTTPS対応されていることがあります。
HerokuもHTTPS対応となりますが、今回は手軽にGithub Pagesでホストすることにしました。
2.service-worker.js
を用意する
PWAの要のひとつであるService workerです。
install時にどのファイルをキャッシュするか、Push通知を受け取って表示するなどの制御を行います。
概要を掴むのはこちらの記事が分かり易かったです。
今回行いたいのは
- インストール時にオフライン動作に必要なファイルのキャッシュする
- オフライン優先で動作
- サイトが更新された場合はキャッシュを更新する
なので、以下のようなservice-worker.js
を用意しました。
// cache name, cache files
var CACHE_NAME = 'saizeriya-cache-v1';
var urlsToCache = [
'/index.html',
'/static/style.css',
'/gacha.js',
'/menu.json.js'
];
// install cache
self.addEventListener('install', function (event) {
event.waitUntil(
caches
.open(CACHE_NAME)
.then(function (cache) {
return cache.addAll(urlsToCache);
})
);
});
// use cache
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.open(CACHE_NAME).then(function (cache) {
return cache.match(event.request).then(function (response) {
return response || fetch(event.request).then(function (response) {
return caches.open(CACHE_NAME).then(function (cache) {
cache.put(event.request, response.clone());
return response;
});
});
});
})
);
});
// refresh cache
self.addEventListener('activate', function (event) {
event.waitUntil(
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames.filter(function (cacheName) {
return cacheName !== CACHE_NAME;
}).map(function (cacheName) {
return caches.delete(cacheName);
})
);
}).then(function () {
clients.claim();
})
);
});
また、ServiceWorkerを登録するためにindex.html
にregisterを仕込みます。
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./service-worker.js').then(function () { console.log('Service Worker Registered'); });
}
</script>
3. manifest.json
を用意する
manifestファイルでは、Webサイトに関する情報をJSON形式で設定します。
アプリ名や表示方法などを設定することができます。
色々設定できるようですが、今回は以下を設定しました。
-
name
: ウェブアプリの名前 -
short_name
: ホーム画面に表示する名前 -
display
: 表示方法 (全画面・横向き固定など) -
start_url
: アプリの起動時のアクセス先 -
icons
: アイコン情報
{
"name": "Saizeriya1000yen-gacha",
"short_name": "Saizeriya1000",
"display": "standalone",
"start_url": "/",
"icons": [
{
"src": "static/apple-touch-icon.png",
"sizes": "192x192",
"type": "image/png"
}
]
}
4. iOS対応をする
こちら少しハマったポイントです。
iOS Safariではmanifest.json
の設定を読み込めないため
少し工夫が必要です。
調べたところ、2つの方法があるようです。
a. metaタグを入れる
全画面表示やアイコンの設定をhtmlにmeta
タグとして直接仕込んでしまう方法です。
<meta name="apple-mobile-web-app-title" content="1000円ガチャ">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<link rel="apple-touch-icon" sizes="180x180" href="static/apple-touch-icon.png">
b. PWA Compatを使う
PWA Compatを使うと、manifestファイルを読み込んでlink
やmeta
を生成してくれます。
今回はこちらを採用しました。
<link rel="manifest" href="manifest.webmanifest" />
...
<script async src="https://cdn.jsdelivr.net/npm/pwacompat" crossorigin="anonymous"></script>
🚀 デプロイ
Github Pagesでまるっとデプロイしてしまいます。
HTTPS対応でリポジトリにpushするだけでよいので、とても便利・・・
今回作成したコードの全体はコチラ↓
https://github.com/marushosummers/Saizeriya_1000yen_pwa
🍵 おわりに
無事、入門の扉を叩けました...!
まだPWAの全体像は掴めていませんが
ちょっとした設定でWebアプリがネイティブアプリっぽくなるのは、とても魅力的です。
まだPWA自体は途上の部分があるようですが
個人制作のWebサイトやブラウザゲームをPWAにする範囲では
一気にアプリ感が出ておトクな気分になりますね。
皆さんもぜひ、PWAを触ってみてください!
☕️ さいごに
僕が所属するestieでは、Python/JavaScript/Rubyエンジニアを積極採用中です!
不動産領域でのプロダクト・データ基盤開発に興味のある方はぜひ、
estie inside blogや採用ページをご覧ください。
Twitterでお気軽なご連絡もお待ちしています〜!