CODE School of Action and Magicへようこそ!
今日のレッスンでは、Web Push + Service Worker(SW)のあまり知られていないタンデムの使用方法を学習します。 私はあなたのためにベールを開きます:Web PushテクノロジーのおかげでMuggleの視聴者を維持する方法と、それがWebサイト編集者やその他のインターネットサービスにどのように役立つかについてお話します。
このテキストは、SWの基本的なアプリケーションに関する記事の続きです。 コメントと質問から、私は人々がSW + Web Pushの方向に興味を持っていることに気付きました。 それが何であるか、そして実際にこの魔法のペアを使用する方法を理解しましょう。
プッシュ通知とは
電子メールへの通知を受け入れます。メールクライアントに移動して、受信したレターを確認します。 この場合、これはプルテクノロジーです。つまり、必要なときにサイトにアクセスして、そこからデータを「プル」します。
プッシュ通知の場合、リソースは新しいデータをあなたにプッシュします。 この場合、この技術ではデータ検証の特定の期間がないため、最新のデータがすぐに受信され、オンラインになります。 プッシュの使用は、通知の受信に限定されません。 そのため、プッシュテクノロジを使用して、更新中にデータを同期できます。
プッシュ通知は小さなポップアップです。 それらは、通知領域がある画面に表示されるか、画面に受信データを表示することができます。 プッシュ通知は、ユーザーがサイトを離れた場合でも機能します。 これは、アプリケーションでユーザーの注意を引くために、新しい素材と新しいイベントに関するメッセージを配信できることを意味します。
ブラウザのサポートを忘れないでください。 そのため、IE、Edge、およびSafariでは、プッシュ通知は機能しません。
SWとWebプッシュの魔法の束の空白
Webプッシュを使用するための独自のSWを作成するには、常に以下が必要です。
必要なことは、sw.jsファイルが登録されるindex.htmlのindex.jsを接続することだけです。
server.jsファイルでは、プッシュ通知を登録するためのエンドポイント(サーバーアプリケーションAPIへのエントリポイント)のみを示します。
サンプルserver.jsファイルコード
// web-push, // push. // // . https://tools.ietf.org/html/draft-ietf-webpush-protocol // https://tools.ietf.org/html/draft-ietf-webpush-encryption. var webPush = require('web-push'); // GCM_API_KEY // https://developers.google.com/cloud-messaging/ webPush.setGCMAPIKey(process.env.GCM_API_KEY || null); // route' express.js module.exports = function(app, route) { app.post(route + 'register', function(req, res) { res.sendStatus(201); }); app.post(route + 'sendNotification', function(req, res) { setTimeout(function() { // payload, 'auth' 'p256dh'. webPush.sendNotification({ endpoint: req.body.endpoint, TTL: req.body.ttl, keys: { p256dh: req.body.key, auth: req.body.authSecret } }, req.body.payload) .then(function() { res.sendStatus(201); }) .catch(function(error) { res.sendStatus(500); console.log(error); }); }, req.query.delay * 1000); }); };
この記事では、プッシュ通知を送信するためのオプションとその使用方法について検討します。 ホグワーツ以外の魔法を一緒に知りましょう。
プッシュペイロード
この最も単純な魔法の呪文は、文字列を送受信する方法を示していますが、データはプッシュメッセージからさまざまな形式で抽出できます:文字列、ArrayBufferバッファ、JOB blobオブジェクト。
申し込み方法
プッシュ通知だけが必要な場合は、この例が適しています。 メッセージは、テキストだけでなく、ペイロード(アプリケーションの充実したデータ)も配信できます。 以下のコードは、アプリケーションにペイロードを配信する方法を示しています。
デモンストレーションでは、テキストフィールドのデータを使用します。このデータはサーバーに送信され、SWを介してプッシュ通知として表示されます。
index.js
var endpoint; var key; var authSecret; navigator.serviceWorker.register('service-worker.js') .then(function(registration) { // PushManager, -. return registration.pushManager.getSubscription() .then(function(subscription) { // . if (subscription) { return subscription; } // , . // userVisibleOnly - , push- // , // . return registration.pushManager.subscribe({ userVisibleOnly: true }); }); }).then(function(subscription) { // public key . var rawKey = subscription.getKey ? subscription.getKey('p256dh') : ''; key = rawKey ? btoa(String.fromCharCode.apply(null, new Uint8Array(rawKey))) : ''; var rawAuthSecret = subscription.getKey ? subscription.getKey('auth') : ''; authSecret = rawAuthSecret ? btoa(String.fromCharCode.apply(null, new Uint8Array(rawAuthSecret))) : ''; endpoint = subscription.endpoint; // Fetch API fetch('./register', { method: 'post', headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ endpoint: subscription.endpoint, key, authSecret, }), }); }); // . // "" , .. . document.getElementById('doIt').onclick = function() { var payload = document.getElementById('notification-payload').value; var delay = document.getElementById('notification-delay').value; var ttl = document.getElementById('notification-ttl').value; fetch('./sendNotification', { method: 'post', headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ endpoint: endpoint, payload: payload, delay: delay, ttl: ttl, key: key, authSecret: authSecret }), }); };
service-worker.js
// 'push' self.addEventListener('push', function(event) { var payload = event.data ? event.data.text() : 'Alohomora'; event.waitUntil( // . self.registration.showNotification('My first spell', { body: payload, }) ); });
豊富な通知
以前のバージョンを複雑にし、特殊効果を追加します。それはすべてあなたの欲求と想像力に依存します。 完全な通知APIが役立ちます。 このAPIは、ロケール、 バイブレーションパターン 、画像を示す「ライブ」プッシュ通知をユーザーに使用するためのインターフェイスを提供します。
申し込み方法
例は上記の例と似ていますが、より高度な通知APIを使用して、画像の選択、ロケールと通知テンプレートの設定、つまり通知を一意にすることができます。
service-worker.js
// Push Payload // Notitfication API SW self.addEventListener('push', function(event) { var payload = event.data // - , // try.. catch ¯\_(ツ)_/¯ ? JSON.parse(event.data) : { name: 'Expecto patronum!', icon: 'buck.jpg', locale: 'en' }; event.waitUntil( // . // : // * // * // * // , // https://notifications.spec.whatwg.org/ self.registration.showNotification('Summoning spell', { lang: payload.locale, body: payload.name, icon: payload.icon, vibrate: [500, 100, 500], }) ); });
プッシュタグ
次の呪文は、以前の呪文をより強力な呪文に蓄積して置き換えることを示します。 通知タグを使用して、古い通知を新しい通知に置き換えます。 Mugglesが関連情報のみを表示するか、複数の通知を1つにまとめることができます。
申し込み方法
このオプションは、チャットまたは新しいコンテンツの通知があるアプリケーションに適しています(例:TprogerおよびTinder)。 以下のコードは、以前の通知を破棄するか、単一の通知に結合できるように、通知キューを管理する方法を示しています。 これは、投稿を編集できるチャットを作成した場合にフォールバックするのに便利です。 クライアントには、修正された通知が大量に表示されるのではなく、1つだけが表示されます。
service-worker.js
var num = 1; self.addEventListener('push', function(event) { event.waitUntil( // . // , . // // ( ) self.registration.showNotification('Attacking Spell', { body: ++num > 1 ? 'Bombarda Maxima' : 'Bombarda', tag: 'spell', }) ); });
プッシュクライアント
「許されない呪文」の時が来ました。 それらが許されない理由を思い出させてください:
マグルでこれらの呪文を過度に使用するとアズカバンでの生活によって罰せられます。 したがって、主なことは気にすることではありません!
マグルがプッシュイベントから生成された通知をクリックすると、アプリケーションタブにフォーカスが移動するか、閉じられていた場合は再び開きます。
申し込み方法
以下は、アプリケーションの状態に応じて、通知配信を使用する3つのケースのコードです。
ページを開いているとき、または開いているタブに切り替えるか、タブを再度開く必要があるときを認識できます。
このメソッドは、マグルを返したい場合に、アプリケーションの変換を増やすのに役立ちます。 あまり遠くに行かないでください、マグルはアプリケーションの過度の注意を好まないかもしれません。
最も古典的な使用例:
- チャットでメッセージが来た(Tinder)、
- 興味深いニュース(Tproger)、
- バグトラッカーのタスクが更新されました。
- リリース前にCIの成功/失敗、
- クライアントが注文の代金を支払い/取引を行いました(オンラインストアまたはCRM)。
これらのすべての場合において、ユーザーがプッシュをクリックすると、クライアントはアプリケーションを開くか、すでに開いているタブにフォーカスします。
service-worker.js
self.addEventListener('install', function(event) { event.waitUntil(self.skipWaiting()); }); self.addEventListener('activate', function(event) { event.waitUntil(self.clients.claim()); }); self.addEventListener('push', function(event) { event.waitUntil( // SW self.clients.matchAll().then(function(clientList) { // , . var focused = clientList.some(function(client) { return client.focused; }); var notificationMessage; if (focused) { notificationMessage = 'Imperio! You\'re still here, thanks!'; } else if (clientList.length > 0) { notificationMessage = 'Imperio! You haven\'t closed the page, ' + 'click here to focus it!'; } else { notificationMessage = 'Imperio! You have closed the page, ' + 'click here to re-open it!'; } // «Unforgiveable Curses» // SW // ( : // * 1, ; // * 2, - , ; // * 3, ). return self.registration.showNotification('Unforgiveable Curses', { body: notificationMessage, }); }) ); }); // 'notificationclick'. self.addEventListener('notificationclick', function(event) { event.waitUntil( // SW. self.clients.matchAll().then(function(clientList) { // , . if (clientList.length > 0) { return clientList[0].focus(); } // . return self.clients.openWindow('our/url/page'); }) ); });
プッシュサブスクリプション
マグルの心を引き継ぐ時です。 マグルはこれを「テレパシー」または読書思考と呼びますが、私たちはそれを別の方法で行います。 情報を入力し、アプリケーションに添付する方法を学びましょう。 この例では、プッシュ通知をサブスクリプション管理で使用して、ユーザーがアプリケーションにサブスクライブして連絡を取り合う方法を示します。 アズカバンについて思い出そうとしています!
申し込み方法
SWが登録された後、クライアントは通知サービスに登録されているかどうかを確認します。 これに応じて、ボタンのテキストが設定されます。
成功したサブスクリプション( index.js :: pushManager.subscribe )の後、クライアントはサブスクリプションを登録するためにアプリケーションサーバーにポストリクエストを送信します。
サーバーは、 Webプッシュライブラリを使用して、登録されているすべてのエンドポイントに定期的に通知を送信します。 エンドポイントが登録されなくなった(サブスクリプションが期限切れまたはキャンセルされた)場合、現在のサブスクリプションはサブスクリプションのリストから削除されます。
正常にサブスクライブを解除した後( index.js :: pushSubscription.unsubscribe )、クライアントはアプリケーションサーバーにポストリクエストを送信してサブスクリプションの登録を解除します。 サーバーは通知を送信しなくなりました。 SWはpushsubscriptionchangeも監視し、 イベントを再サブスクライブします 。
サブスクリプションは、ブラウザー設定またはUI通知でページ外のユーザーがキャンセルできます。 この場合、バックエンドは通知の送信を停止しますが、フロントエンドはそれを認識しません。 マジックが機能するためには、通知サービスへの登録がアクティブかどうかを定期的に確認することが重要です。
index.js
// . // . var subscriptionButton = document.getElementById('subscriptionButton'); // , , // Promise. function getSubscription() { return navigator.serviceWorker.ready .then(function(registration) { return registration.pushManager.getSubscription(); }); } if ('serviceWorker' in navigator) { navigator.serviceWorker.register('service-worker.js') .then(function() { console.log('SW registered'); subscriptionButton.removeAttribute('disabled'); }); getSubscription() .then(function(subscription) { if (subscription) { console.log('Already invaded', subscription.endpoint); setUnsubscribeButton(); } else { setSubscribeButton(); } }); } // «registration» SW // `registration.pushManager.subscribe`. // , POST-. function subscribe() { navigator.serviceWorker.ready.then(function(registration) { return registration.pushManager.subscribe({ userVisibleOnly: true }); }).then(function(subscription) { console.log('Legilimens!', subscription.endpoint); return fetch('register', { method: 'post', headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ endpoint: subscription.endpoint }) }); }).then(setUnsubscribeButton); } // SW, // (`subscription.unsubscribe ()`) // POST- // push-. function unsubscribe() { getSubscription().then(function(subscription) { return subscription.unsubscribe() .then(function() { console.log('Unsubscribed', subscription.endpoint); return fetch('unregister', { method: 'post', headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ endpoint: subscription.endpoint }) }); }); }).then(setSubscribeButton); } // ( ). . function setSubscribeButton() { subscriptionButton.onclick = subscribe; subscriptionButton.textContent = 'Open mind!'; } function setUnsubscribeButton() { subscriptionButton.onclick = unsubscribe; subscriptionButton.textContent = 'Protego!'; }
service-worker.js
// 'push'. self.addEventListener('push', function(event) { event.waitUntil(self.registration.showNotification('Your mind', { body: 'Wizard invaded to your mind!' })); }); // 'pushsubscriptionchange', , // . // , // POST-. // ID token // . self.addEventListener('pushsubscriptionchange', function(event) { console.log('Spell expired'); event.waitUntil( self.registration.pushManager.subscribe({ userVisibleOnly: true }) .then(function(subscription) { console.log('Another invade! Legilimens!', subscription.endpoint); return fetch('register', { method: 'post', headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ endpoint: subscription.endpoint }) }); }) ); });
server.js
var webPush = require('web-push'); var subscriptions = []; var pushInterval = 10; webPush.setGCMAPIKey(process.env.GCM_API_KEY || null); // push-. // `subscriptions`, // push- . function sendNotification(endpoint) { webPush.sendNotification({ endpoint: endpoint }).then(function() { }).catch(function() { subscriptions.splice(subscriptions.indexOf(endpoint), 1); }); } // // . // , `pushInterval` // setInterval(function() { subscriptions.forEach(sendNotification); }, pushInterval * 1000); function isSubscribed(endpoint) { return (subscriptions.indexOf(endpoint) >= 0); } module.exports = function(app, route) { app.post(route + 'register', function(req, res) { var endpoint = req.body.endpoint; if (!isSubscribed(endpoint)) { console.log('We invaded into mind ' + endpoint); subscriptions.push(endpoint); } res.type('js').send('{"success":true}'); }); // Unregister a subscription by removing it from the `subscriptions` array app.post(route + 'unregister', function(req, res) { var endpoint = req.body.endpoint; if (isSubscribed(endpoint)) { console.log('It was counterspell from ' + endpoint); subscriptions.splice(subscriptions.indexOf(endpoint), 1); } res.type('js').send('{"success":true}'); }); };
呪文についてもう一度
上記では、アプリケーションにSWとWebプッシュを使用する魔法の方法について見てきました。
このタンデムには、多くの興味深いアプリケーションがたくさんあります。
アプリケーションに時々マグルを呼び出す必要がある場合や、注文のステータスの修正や変更について通知する必要がある場合は、プッシュペイロードを使用します。 少し想像力を追加し、通知APIを使用できます-アプリケーションの色とアイコンがリッチプッシュでユーザーに表示されます。
あなたがマグルのすべての注意を引き付け、彼との接触を確立したい場合-プッシュクライアントとプッシュサブスクリプションの例はあなたのためです。 主なことは、アズカバンを覚えることです。さもないと、視聴者を失い始めます。
次のトピックに関するコメントと提案をお待ちしています。 SW + React / Reduxアプリケーションと高速化の方法のトピックについて話し、話したいと自分で付け加えます。 役に立つでしょうか?