Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
PWAの基本とTips
Search
Atsushi Nakatsugawa
PRO
October 18, 2018
Technology
0
77
PWAの基本とTips
Monaca UG OHMIYA #3 ~集まれMonaca ユーザー!~
https://monacaug.connpass.com/event/98537/
の発表資料です。
Atsushi Nakatsugawa
PRO
October 18, 2018
Tweet
Share
More Decks by Atsushi Nakatsugawa
See All by Atsushi Nakatsugawa
DevRelの基礎と戦略
moongift
PRO
0
18
DevRelの始め方
moongift
PRO
3
520
マンガで分かるDevRelオンライン
moongift
PRO
0
110
DevRel Online with Manga
moongift
PRO
0
65
DevRel 4コマ
moongift
PRO
0
100
DevRel 4 panel Manga
moongift
PRO
0
62
DevRelの基礎〜開発者マーケティング〜
moongift
PRO
0
25
DevRelの基礎〜DevX(開発者体験)〜
moongift
PRO
0
35
DevRelの基礎「DevRelの重要性」
moongift
PRO
0
37
Other Decks in Technology
See All in Technology
100 名超が参加した日経グループ横断の競技型 AWS 学習イベント「Nikkei Group AWS GameDay」の紹介/mediajaws202411
nikkei_engineer_recruiting
1
170
ドメインの本質を掴む / Get the essence of the domain
sinsoku
2
150
個人でもIAM Identity Centerを使おう!(アクセス管理編)
ryder472
3
210
いざ、BSC討伐の旅
nikinusu
2
780
Shopifyアプリ開発における Shopifyの機能活用
sonatard
4
250
CysharpのOSS群から見るModern C#の現在地
neuecc
2
3.3k
マルチモーダル / AI Agent / LLMOps 3つの技術トレンドで理解するLLMの今後の展望
hirosatogamo
37
12k
Lexical Analysis
shigashiyama
1
150
DMARC 対応の話 - MIXI CTO オフィスアワー #04
bbqallstars
1
160
強いチームと開発生産性
onk
PRO
34
11k
【若手エンジニア応援LT会】ソフトウェアを学んできた私がインフラエンジニアを目指した理由
kazushi_ohata
0
150
Introduction to Works of ML Engineer in LY Corporation
lycorp_recruit_jp
0
120
Featured
See All Featured
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
6.8k
The Power of CSS Pseudo Elements
geoffreycrofte
73
5.3k
Happy Clients
brianwarren
98
6.7k
Music & Morning Musume
bryan
46
6.2k
How to Think Like a Performance Engineer
csswizardry
20
1.1k
GitHub's CSS Performance
jonrohan
1030
460k
Unsuck your backbone
ammeep
668
57k
A Tale of Four Properties
chriscoyier
156
23k
Being A Developer After 40
akosma
86
590k
Mobile First: as difficult as doing things right
swwweet
222
8.9k
Site-Speed That Sticks
csswizardry
0
25
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
Transcript
PAGE DAY 2017/11/01 # MOONGIFT X / 12 PWAの基本とTips
DAY 2018/09/10 # MOONGIFT 自己紹介 @goofmint fb.me/goofmint 中津川 篤司 株式会社MOONGIFT
代表取締役 www.moongift.jp エバンジェリスト コミュニティ
PAGE DAY 2017/11/01 # MOONGIFT X / 12 PWAとは? •
Progressive Web Appsの略 • Webアプリケーションをよりスマートフォンアプリに近づけ る仕組み • 特定の技術のことではない • Monacaの近いうちに対応するらしいよ
PAGE DAY 2017/11/01 # MOONGIFT X / 12
PAGE DAY 2017/11/01 # MOONGIFT X / 12
PAGE DAY 2017/11/01 # MOONGIFT X / 12 PWAの要素 1.
App Manifest 2. Service Worker 3. プッシュ通知 4. indexedDB 5. その他
PAGE DAY 2017/11/01 # MOONGIFT X / 12 ΞϓϦԽ
PAGE DAY 2017/11/01 # MOONGIFT X / 12 必要な技術 •
App Manifest • Service Worker • SSL/TLS • Android / Google Chromeは対応 • iOS Safariは微妙に対応
PAGE DAY 2017/11/01 # MOONGIFT X / 12 App Manifest
• アプリの仕様(マニフェスト)を記述したファイル • アプリ名 • アイコン • 最初に表示するURL • 背景色 • テーマ色
PAGE DAY 2017/11/01 # MOONGIFT X / 12
PAGE DAY 2017/11/01 # MOONGIFT X / 12 manifest.jsonの例 {
"short_name": "短いアプリ名", "name": "長いアプリ名", "icons": [ { "src": "アイコン.png", "sizes": "192x192", "type": "image/png" } ], "start_url": "./index.html", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" } <link rel="manifest" href="/manifest.json">
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Google Chromeで見る
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Service Worker
• WebブラウザのJavaScriptとは別プロセスで動くJavaScript • 主にキャッシュ、プッシュ通知の受信で利用 • ネットワーク(fetch)/indexedDBが利用可能 • DOMは利用不可 • メッセージでWebブラウザのJavaScript通信可
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Service Workerの登録
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/serviceworker.js') .then(registration => { // 登録成功 }).catch(err => { // 登録失敗 }); }
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Service Workerでの処理
とりあえずキャッシュ処理 const CACHE_NAME = 'SALES_SYSTEM'; const urls = [ ‘/', ‘/styles/app.css', '/script/app.js' ]; self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => { return cache.addAll(urls); }) ); });
PAGE DAY 2017/11/01 # MOONGIFT X / 12 ネットワークアクセス self.addEventListener('fetch',
await (event) => { event.respondWith( const response = await caches.match(event.request) if (response) return response; return fetch(event.request); ); });
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Tips 1
: キャッシュ • URL と レスポンスのKVS • キャッシュを普通に使うとGETリクエストのみ • 署名生成とかは非現実的なのでWebブラウザから送るのがポ イント
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Tips2:キャッシュ •
キャッシュは二回目以降のアクセスで有効 • オンライン/オフライン関係なく利用される
PAGE DAY 2017/11/01 # MOONGIFT X / 12 ハマった例 •
NCMBでは署名処理 -> ヘッダーに追加してアクセス • キャッシュ(fetch)はヘッダーなしでアクセス -> 404 • 二回目以降のアクセスはオンラインでも404が返ってくる
PAGE DAY 2017/11/01 # MOONGIFT X / 12 動的キャッシュ(Web) const
channel = new MessageChannel(); navigator.serviceWorker.controller .postMessage({ action: 'cache', url: res.req.url, response: body }, [channel.port2]);
PAGE DAY 2017/11/01 # MOONGIFT X / 12 動的キャッシュ(Service Worker)
self.addEventListener('message', e => { if (e.data.action == 'cache') { caches.open(CACHE_NAME) .then((cache) => { const res = new Response(e.data.response); cache.put(e.data.url, res); }) } });
PAGE DAY 2017/11/01 # MOONGIFT X / 12 プッシュ通知
PAGE DAY 2017/11/01 # MOONGIFT X / 12 プッシュ通知の種類 •
ローカルプッシュ Notifications API • Chrome向けプッシュ Firebase必須 • macOS Safari向けプッシュ 証明書必須 • Push API/VAPID Chrome/Firefox/Edge
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Notifications API
• サーバ不要 • ユーザの確認は必要 • 開いているタブに対してのみ • チャット、Webアプリの処理完了など
PAGE DAY 2017/11/01 # MOONGIFT X / 12 状態の確認 console.log(Notification.permission);
// -> default // -> granted // -> denied
PAGE DAY 2017/11/01 # MOONGIFT X / 12 許可を得る Notification.requestPermission(permission
=> { if (permission === "granted") { // 許可した場合 } // denied });
PAGE DAY 2017/11/01 # MOONGIFT X / 12 プッシュ通知を送信 const
options = { body: 'メッセージ本文', icon: 'img/icon.png' } const n = new Notification('メッセージタイトル', options);
PAGE DAY 2017/11/01 # MOONGIFT X / 12 プッシュ通知を自動で閉じる const
n = new Notification(‘title', options); setInterval(() => { n.close.bind(n) }, 3000);
PAGE DAY 2017/11/01 # MOONGIFT X / 12 プッシュ通知のイベント •
onclick 通知をクリックした時に呼ばれます • onerror エラーが出た時に呼ばれます • onclose 閉じた際に呼ばれます • onshow 表示した時に呼ばれます
PAGE DAY 2017/11/01 # MOONGIFT X / 12 クリックしたら消す const
options = { body: 'メッセージ本文', icon: 'img/icon.png' } const n = new Notification('メッセージタイトル', options); n.onclick = (event) => { // クリックしたら通知を消す n.close.bind(n); }
PAGE DAY 2017/11/01 # MOONGIFT X / 12 独自プッシュ Google
ChromeのFirebase、macOS Safariのプッシュ通知 今回はスルー
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Push API/VAPID
• Firebase不要 • Android / Google Chrome / Firefox / Edge対応 • サーバ必要 • Service Worker必須
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Push API/VAPIDの流れ
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Push API/VAPIDの流れ
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Push API/VAPIDの流れ
Firebase
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Push API/VAPIDの流れ
Firebase 4FSWJDF 8PSLFS
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Push API/VAPIDの流れ
Firebase 4FSWJDF 8PSLFS
PAGE DAY 2017/11/01 # MOONGIFT X / 12 受信状態を知る if
(!('serviceWorker' in navigator)) return; await navigator.serviceWorker.register('./serviceworker.js') if (!('showNotification' in ServiceWorkerRegistration.prototype)) return; if (Notification.permission === 'denied') return; if (!('PushManager' in window)) return; const serviceWorkerRegistration = await navigator.serviceWorker.ready; const subscription = await serviceWorkerRegistration.pushManager .getSubscription(); if (subscription) { // すでに購読中 } else { // 未購読 }
PAGE DAY 2017/11/01 # MOONGIFT X / 12 承認を得る const
serviceWorkerRegistration = await navigator.serviceWorker.ready const subscription = await serviceWorkerRegistration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: convertedVapidKey }); console.log(subscription.toJSON());
PAGE DAY 2017/11/01 # MOONGIFT X / 12 JSONが得られる {
"endpoint":"https://fcm.googleapis.com/fcm/send/e7K...Hag", "expirationTime":null, "keys":{ "p256dh":"BK7...e5U", "auth":"DGt...D8g" } }
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Service Workerの処理
self.addEventListener('install', () => { console.log('ServiceWorker がインストールされました'); }); self.addEventListener('push', ev => { // payloadの取得 const {title, msg, icon} = ev.data.json(); self.registration.showNotification(title, { icon: icon, body: msg }); });
PAGE DAY 2017/11/01 # MOONGIFT X / 12 URLを指定して実行 const
webpush = require("web-push"); const keys = require("./application-server-keys.json"); webpush.setVapidDetails( "mailto:
[email protected]
", keys.publicKey, keys.privateKey ); const subscribers = [/* さっきのJSON */]; const icon = `app.png`; const params = { title: "プッシュ通知です!", msg: `これはサーバから送っています. 今は ${new Date().toLocaleString()} です。 メッセージとアイコンも送っています `, icon: icon }; const res = await Promise.all(subscribers.map(subscription => { return webpush.sendNotification(subscription, JSON.stringify(params), {}); }));
PAGE DAY 2017/11/01 # MOONGIFT X / 12 受信(Service Worker)
self.addEventListener('install', () => { console.log('ServiceWorker がインストールされました'); }); self.addEventListener('push', ev => { // payloadの取得 const {title, msg, icon} = ev.data.json(); self.registration.showNotification(title, { icon: icon, body: msg }); });
PAGE DAY 2017/11/01 # MOONGIFT X / 12 プッシュ通知の表示
PAGE DAY 2017/11/01 # MOONGIFT X / 12 便利なライブラリ •
Node.js https://github.com/web-push-libs/web-push • Ruby https://github.com/zaru/webpush
PAGE DAY 2017/11/01 # MOONGIFT X / 12 データベース
PAGE DAY 2017/11/01 # MOONGIFT X / 12 データベースの種類 $PPLJF
MPDBM4UPSBHF JOEFYFE%# αΠζ ,# .# .# ͍ํ ಉظ ಉظ ඇಉظ 8FCϒϥβ 㾎 㾎 㾎 4FSWJDF8PSLFS º º 㾎 ܕ จࣈྻͷΈ จࣈྻͷΈ ˓
PAGE DAY 2017/11/01 # MOONGIFT X / 12 スキーマ let
db; let version = 1; const request = indexedDB.open("database", version); request.onerror = function(event) { alert("IndexedDB が使えません"); }; request.onupgradeneeded = function(event) { db = event.target.result; const objectStore = db.createObjectStore("items", { keyPath: "id" }); objectStore.createIndex("name", "name", { unique: false }); objectStore.createIndex("description", "description", { unique: true }); }; request.onsuccess = function(event) { db = event.target.result; }
PAGE DAY 2017/11/01 # MOONGIFT X / 12 データ追加 const
transaction = db.transaction(["items"], "readwrite"); transaction.oncomplete = (event) => { console.log("完了しました"); }; transaction.onerror = (event) => { console.log("エラーです", event); }; const objItem = transaction.objectStore("items"); for (let i = 1; i < 10; i += 1) { objItem.add( { id: i, name: `テスト商品 ${i}`, description: `テスト商品 ${i} の説明`, add: '追加' } ).onsuccess = (event) => { console.log(event.target.result); } }
PAGE DAY 2017/11/01 # MOONGIFT X / 12 データ検索 db.transaction(["items"])
.objectStore("items") .get(3) .onsuccess = (event) => { console.log("取得しました", event.target.result); }
PAGE DAY 2017/11/01 # MOONGIFT X / 12 データ更新 const
Items = db.transaction(["items"], "readwrite") .objectStore("items"); Items.get(3) .onsuccess = (event) => { const row = event.target.result; row.name = "商品3の名前を変更"; Items .put(row) .onsuccess = (event) => { console.log("更新しました"); } }
PAGE DAY 2017/11/01 # MOONGIFT X / 12 データ削除 db.transaction(["items"],
"readwrite") .objectStore("items") .delete(2) .onsuccess = (event) => { console.log("削除完了しました"); }
PAGE DAY 2017/11/01 # MOONGIFT X / 12 使うと便利なライブラリ •
Lovefield by Google https://github.com/google/lovefield • JS Store http://jsstore.net
PAGE DAY 2017/11/01 # MOONGIFT X / 12 その他のAPI
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Credential Management
API 認証情報をブラウザに保存、呼び出すAPI const form = document.querySelector('#reg-form') const cred = new PasswordCredential(form); const credential = await navigator.credentials.store(cred);
PAGE DAY 2017/11/01 # MOONGIFT X / 12 認証情報取得 const
credential = await navigator.credentials.get({ password: true }) console.log(credential) /*{ iconURL: "", id: "
[email protected]
", name: "atsushi", password: "demo", type: "password" }*/
PAGE DAY 2017/11/01 # MOONGIFT X / 12 認証情報取得(外部サイト) const
credential = await navigator.credentials.get({ password: true, federated: { providers: ['https://example.com'] } }) console.log(credential) /*{ iconURL: "https://lh5.goog.com/-y0...6-c/photo.jpg", id: "
[email protected]
", name: "中津川篤司", protocol: "", provider: "https://accounts.google.com", type: "federated" }*/
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Web Authentication
API ハードウェアや外部デバイスを用いた認証をサポート
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Payment Request
API 決済情報をWebブラウザ内に保存、取得するAPI const request = new PaymentRequest(supportedInstruments, details); const result = await request.show(); await fetch('/pay', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(result.toJSON()) });
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Web Share
API スマートフォンでネイティブアプリ風にコンテンツをシェア できるAPI navigator.share({ title: 'Web Fundamentals', text: 'Check out Web Fundamentals — it rocks!', url: 'https://developers.google.com/web', })
PAGE DAY 2017/11/01 # MOONGIFT X / 12 便利なTips
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Lighthouse https://developers.google.com/web/tools/lighthouse/
PAGE DAY 2017/11/01 # MOONGIFT X / 12 自分でキャッシュを作る •
navigator.onLine を使う • オンラインの時に取得して indexedDB に入れておく • POST/PUT/DELETEの時に便利
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Androidで確認する chrome://flags
で Bypass user engagement checks を有効にする
PAGE DAY 2017/11/01 # MOONGIFT X / 12 オフラインを再現する Chromeの開発者ツールでOfflineを有効にする
PAGE DAY 2017/11/01 # MOONGIFT X / 12 デスクトップでも使う
PAGE DAY 2017/11/01 # MOONGIFT X / 12 Thanks! !