Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
PWAの基本とTips
Search
Atsushi Nakatsugawa
PRO
October 18, 2018
Technology
120
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
PWAの基本とTips
Monaca UG OHMIYA #3 ~集まれMonaca ユーザー!~
https://monacaug.connpass.com/event/98537/
の発表資料です。
Atsushi Nakatsugawa
PRO
October 18, 2018
More Decks by Atsushi Nakatsugawa
See All by Atsushi Nakatsugawa
開発を高速化。AIコーディングエージェントとCodeRabbitでループエンジニアリング
moongift
PRO
0
93
開発を高速化。AIコーディングエージェントとCodeRabbitでループエンジニアリング
moongift
PRO
0
150
AIコーディングの次。コードレビューと理解負荷を解消して組織の開発生産性を高める
moongift
PRO
2
3k
AI駆動開発は個人技からチーム戦へ:組織でAIを使いこなすための実践設計
moongift
PRO
0
720
AI時代の開発生産性は、個人技からチーム設計へ
moongift
PRO
6
4k
コーディングだけ速くとも意味がない。自動レビューによる開発の高速化
moongift
PRO
0
150
AI 支援研究開発におけるコードレビューの役割と限界
moongift
PRO
0
230
個人AIからチームAIへ:開発における品質と生産性の再設計
moongift
PRO
0
540
AI活用の格差をなくす:チーム全体のAI開発生産性を底上げする方法
moongift
PRO
1
850
Other Decks in Technology
See All in Technology
go-sqletch
moznion
0
140
Tofu-Controller lightning talk @ Containerday 2026
gionn
0
110
フロントエンドカンファレンス関西2026 - 速すぎるUIに、待ち時間を
teamlab
PRO
0
130
「Docker Desktop不要の時代」は 来たのか?
ueponx
0
770
スプシの手作業からの解脱 ~更新はClaude Codeに、見せ方と使い方はGASアプリに任せる~
kentarofujii
0
150
WasmをRustにする
sansantech
PRO
0
250
OptunaHub: ブラックボックス最適化のためのプラットフォーム
pfn
PRO
0
140
パッチ更新でterraform initが落ちた日
grimoh
1
140
コンテナ運用におけるネットワーク・コンテナコストの削減方法/How to Reduce Network and Container Costs in Container Operations
masakiokuda
1
180
実践!Cloud Run ハンズオン!! / Cloud Run handson
y0hgi
2
280
しろおびから始める!ログ活用の第一歩
shumei_ito
0
950
無理せず続ける、子育てエンジニアの学び
codmoninc
PRO
0
870
Featured
See All Featured
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6.1k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
22k
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
Building the Perfect Custom Keyboard
takai
2
910
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
1
280
RailsConf 2023
tenderlove
30
1.6k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.2k
What's in a price? How to price your products and services
michaelherold
247
13k
The Invisible Side of Design
smashingmag
301
52k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.5k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
410
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! !