Edited at

Service Worker入門〜導入方法から事例紹介まで〜@オトナのプログラミング勉強会

More than 1 year has passed since last update.


はじめに


  • Service Workerって聞いたことあります?



Service Workerとは


  • Webページとは別にバックグランドで実行するスクリプト


    • DOMに直接アクセスできない

    • ブラウザを開いていなくても動作可能

    • プログラム可能なネットワークプロキシとして動作可能

    • HTTPS必須(またはlocalhost)





どんな時に使える?

Cache APIやPush API、Fetch APIと一緒に使うとこで下記のようなことが可能になる


  • キャッシュ・アセットの制御

  • プッシュ通知

  • オフライン対応

  • PWA対応



利用事例(1)



利用事例(2)



利用事例(3)



利用事例(4)



どこでも使えるの?



技術紹介



Service Workerのライフサイクル



Service Workerのライフサイクル(1)


  • 1. 登録


    • jsファイルを指定



  • 2. インストール(oninstall)


    • 静的なアセットを読み込んだりする



  • 3. アクティベート(onactivate)


    • このステップが終わるとすコプ内の全てのページのコントロールが完了

    • 初回はコントロールされず、次に読み込まれた際にコントロールされる





1. 登録

navigator.serviceWorker.register('/sw.js').then(function(registration) {

console.info('registration', registration);
})



2. インストール(oninstall)

self.addEventListener('install', function (e) {

console.info('install', e);
});



2. アクティベート(onactivate)

self.addEventListener('activate', function (e) {

console.info('activate', e);
});



Service Workerのライフサイクル(2)


  • ブラウザスレッド⇔Service Worker間のイベント


    • フェッチ(onfetch)


      • クライアントからサーバーアクセスの際に呼ばれる



    • メッセージ(onmessage)


      • ブラウザスレッドのメソッドからのメッセージング







Service Workerのライフサイクル(3)


  • ブラウザーネットワーク⇔Service Worker間のイベント


    • シンク(onsync)


      • オンライン時に呼ばれる



    • プッシュ(onpush)


      • プッシュ通知を受け取った時に呼ばれる







ハンズオン①


  • Servie Workerを登録&起動の確認



事前準備


  • npmがインストールされている(または、何かしらの言語でサーバーがたてられればOK)

  • 開発はChromeのゲストモード

  • 最終的なレポジトリ





プロジェクトの作成&インストール等


  • プロジェクト作成 


bash

$ mkdir otona_sw

$ cd otona_sw


  • htmlファイルを作成


index.html

<!DOCTYPE html>

<html lang="ja">
<head>
<meta charset="UTF-8">
<title>さーびすわーかー</title>
</head>
<body>
さーびすわーかー
</body>
</html>


  • package.jsonを作成


package.json

{

"scripts": {
"start": "http-server -c-1"
},
"devDependencies": {},
"dependencies": {
"http-server": "^0.11.1"
}
}


  • パッケージのインストール&サーバーの起動

$ npm install

$ npm start


スクリーンショット 2018-05-09 18.13.20.png



Service Workerの登録


sw.js


// インストール
self.addEventListener('install', function (e) {
console.info('install', e);
});

// アクティベート
self.addEventListener('activate', function (e) {
console.info('activate', e);
});

// フェッチ
self.addEventListener('fetch', function (e) {
console.info('fetch', e);
});



index.html

<script>

// Service Worker API が存在しているかをチェック
if ('serviceWorker' in navigator) {
console.log('service worker is active');
navigator.serviceWorker.register('/sw.js').then(function (registration) {
// 登録成功
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function (err) {
// 登録失敗 :(
console.log('ServiceWorker registration failed: ', err);
});
}
</script>



1回目

スクリーンショット 2018-05-09 18.14.44.png


2回目

スクリーンショット 2018-05-09 18.14.49.png



Service workers

スクリーンショット 2018-05-09 18.16.12.png




ハンズオン②


  • リクエストを横取り&レスポンスを生成



index.html

<!-- ...省略 -->

<body>
<!-- ...省略 -->
<a href="test">横取り!</a>
<!-- ...省略 -->
</body>


sw.js

// ...省略

// フェッチ
self.addEventListener('fetch', function (e) {
console.info('fetch', e);

if (e.request.url.indexOf('test') != -1) {
e.respondWith(new Response('Hello world'));
}
});




実行結果

スクリーンショット 2018-05-09 19.30.30.png



ハンズオン③


  • 画像をキャッシュ



注意


  • 一度ゲストモードのブラウザを閉じてください



インストールイベントで画像をキャッシュ


index.html

<!-- ...省略 -->

<body>
<!-- ...省略 -->
<div id="content"></div>
<script>
// ...省略
const element = document.querySelector('#content');
for(let i=1;i<=100;i++){
const image = document.createElement('img')
image.setAttribute('src', `https://robohash.org/${i}.png?size=100x100`)

element.appendChild(image)
}
// ...省略
</script>



sw.js

let urlsToCache = [

'/',
];

for(let i=1;i<=100;i++){
urlsToCache.push(`https://robohash.org/${i}.png?size=100x100`)
}

// インストール
self.addEventListener('install', function (e) {
console.info('install', e);
e.waitUntil(
caches.open('v1').then((cache) => {
// 画像をキャッシュ対象に追加
cache.addAll(urlsToCache)
})
)
});

// フェッチ
self.addEventListener('fetch', function (e) {
console.info('fetch', e);
e.respondWith(
caches.match(e.request)
.then(function (response) {
// キャッシュがあったのでそのレスポンスを返す
if (response) {
console.info(`Using cache: ${e.request.url}`);
return response;
}
return fetch(e.request);
}
)
);

});




1回目と2回目を試す&オフラインで試す

スクリーンショット 2018-05-09 18.49.17.png



参考