Service Worker、はじめの一歩 第1回 Service Workerとは

Service Workerは、Webページの裏側で働く独立したJavaScript環境です。今回はその概要と、どのようなライフサイクルを持っているのかを解説します。全体像が把握しやすくなるはずです。

発行

著者 山田 順久 フロントエンド・エンジニア
Service Worker、はじめの一歩 シリーズの記事一覧

はじめに

このシリーズで扱うService Workerとは、Webページの裏側で働く独立したJavaScript環境です。Service Workerを利用すると、Webページのオフライン対応をはじめ、これまでのWebではできなかった機能が実現できます。

Service Workerは2016年10月現在、Chrome、Firefox、Operaの各ブラウザがすでにサポートをしています。Microsoft Edgeは開発中、Safariは検討を進めています。それぞれのブラウザの実装状況はIs ServiceWorker ready?のページで確認できます。

仕様としては2014年の5月にドラフトが公開されていて、最新版は2015年の6月に公開されています。

また現在、Progressive Web Apps(略してPWAとも呼ばれる)という言葉を聞いたことがある方もいるかと思います。これは簡単にいえば、モバイル用Webアプリケーションという文脈において、ネイティブのように動作するものを作ろうという考えです。PWA自体はなにか新しい機能を指すものではなく、ただブラウザの機能の集合に名前を付けたものです。

そんなPWAの構成要素としてService Workerはなかなか大事な位置にある技術で、これからのWebの体験を向上させる技術として注目に値するものだと筆者は思います。

Service Workerとは

先に述べたように、Service Workerとは、Webページのバックグラウンドで動くもう一つのJavaScript環境です。Service Workerが一度Webページからインストールされると、Webページとは独立したライフサイクルの中で動作します。たとえば、オフライン状態でも、Webページを表示していたタブが閉じていても、必要があればService Workerが作動します。

これでどんなことができるようになるのかというと、次のような例が挙げられます。

  • Webページからのリクエストに反応し、以前に取得していたキャッシュを返却することでオフラインな状況でも動作する
  • Webページを開かなくてもバックグラウンドで最新の情報に更新する
  • Webページからのプッシュ通知を受け取る

ただ、これらのことはService Workerだけで実現する機能ではありません。Service Workerを土台にして、Fetch API、Cache API、Push APIといった別の機能を組み合わせることで実現する機能です。

Service Worker自体は、最初に書いたとおりWebページの裏側で動くJavaScript環境でしかありません。ですが、そんなService Workerと、ほかの新たな機能を組み合わせることで、これまでのWebにはできなかった機能を提供できる可能性を持っています。

Service Workerのライフサイクル

ライフサイクルとは

Service WorkerはWebページから独立したライフサイクルの中で動作すると前述しました。ライフサイクルとは、一般的には、生物が生まれてから死んでいくまでの流れや、ある製品の発売から売上が変化していく様子を表したものなどを指す言葉です。プログラミングにおいてはたいてい、あるプログラムが起動して終了するまでの間、そこで起こる出来事、その周期を指します。

Webページでいえば、あるページが開かれて閉じられるまでの間、JavaScriptのコードでいえば、あるインスタンスを初期化してそれが破棄されるまでの間、という具合になるでしょう。

そして、ライフサイクルの中ではそうしたものたち自身に関するさまざまなイベントが発生します。Webページであればページが開かれる、ページが遷移する、ページが閉じられるなどのイベントです。普段、開発者はそうしたイベントに対して処理を行うコードを書いていく場面があるでしょう。

Service Workerも同じようにライフサイクルを持ち、その中でイベントが発生していきます。Service Workerは、Webページやその中にあるDOMのように、目で見てその状態を把握しやすいものではありません。ですので、どのようなライフサイクルやイベントを持っているのかを把握しておくことで、Service Workerの全体像も理解しやすくなると思います。

Service Workerが持つ6つの状態

それでは、Service Workerが持っているライフサイクルの説明に移ります。Service Workerでは、parsed、installing、installed、activating、activated、redundantの6つの状態があります。

parsed

parsedは初期状態です。Service Workerはまだインストールされておらず、まだService Workerとして呼び出されたスクリプトが読み込まれたくらいの段階です。すぐさま次の状態へ遷移するため、あまり意識することはありません。

installing

installingは名前のとおり、Service Workerをインストールしている最中の状態です。Service Workerの新規インストールか、あるいは更新されている場合にもこの状態をとります。

この際にService Workerのライフサイクルイベントであるinstallイベントが発生しており、Service Worker側のJavaScriptではこのイベントをハンドリングして必要に応じた(たとえばキャッシュを構築するような)インストール処理を行うこともできます。

すでに有効なService Workerがインストール済みで、更新もされていない場合にはactivatedまでスキップされます。

installed

installedは先のinstalling状態を問題なく通過した状態です。この段階でService Workerのインストールには成功していますが、まだService Workerは有効になっていません。

Service Workerを新規インストールしている場合には、すぐに次のactivating状態へと遷移します。Service Workerを更新してる場合には、古いService WorkerがまだWebページを制御しているので、新しいService Workerはこの状態に留まります。

この状態に留まった新しいService Workerが有効になるには、ユーザーがWebページから離れるなどして、ブラウザが安全に古いService Workerを解放できた後に再度Webページを開くことで、次のactivating状態に移る必要があります。また、この状態はオンライン上のドキュメントではwaitingと呼ばれることも多いです。

activating

installedの次の状態がactivatingです。Service Workerの新規インストール時には前段階のinstalledからすぐにこの状態になります。更新の場合には、古いService Workerに代わって新しいService Workerが有効になる段階です。

installingのときと同じように、activating時にはライフサイクルイベントのactivateイベントが発生します。Service Worker側のJavaScriptではイベントをハンドリングして必要に応じた処理(たとえば古いキャッシュを破棄するような)をすることもできます。

activated

Service Workerが、対象のWebページを問題なく制御下に置いた状態です。

この状態でService WorkerはWebページからのリクエストに反応するfetchや、Webページからのメッセージを受け取るmessageイベントを待つことができます。

redundant

次の理由でService Workerが無効となった状態です。

  • installイベント中にエラーが発生した
  • activateイベント中にエラーが発生した
  • 新しいService Workerと置き換えられた

Service Workerのセキュリティ

Service Workerには、セキュリティ上の安全を守るため、その制御をどのページにまで及ぼすことができるのかが定められています。

HTTPS(またはlocalhost)で動かすこと

Service Workerの機能はとても強力です。キャッシュを利用したオフライン対応などの用途に使うのであればよいのですが、悪意を持った者がWebページのリクエストを盗み取るためにService Workerを利用しては問題です。そのため、Service WorkerはHTTPS(あるいはlocalhost)で提供されているWebページでしか登録できません。これによって、Service Workerが改ざんを受けていないことを保証しています。

同一オリジンポリシーにしたがう

Service Workerは読み込まれたオリジンと同一のオリジンを持つWebページにしか効力を持ちません。たとえば、https://www.pxgrid.com/から登録されたService Workerは、https://app.codegrid.net/にあるWebページを制御下に置くことはできません。

オリジンが同一であるというのは、ホストだけでなくURIスキームとポートも同一であることが求められます。ですので、スキーム違いとなるhttp://app.codegrid.net/https://app.codegrid.net/、ポート違いとなるhttps://app.codegrid.net:80/https://app.codegrid.net:81/は同一オリジンとなりません。

スコープにしたがう

Service Workerはその役割を担うスクリプトファイルが置かれているパスよりも上位のパスにあるWebページを制御下に置くことは、基本的にはできません。

Service Workerのスクリプトが/sw.jsにあるのなら、同一オリジンにあるすべてのWebページを制御下に置くことができます。

そうではなく、スクリプトが/scope/sw.jsにあるのなら、/scope/index.html/scope/foo/bar.htmlのように/scope/より下層のWebページのみを制御できます。/out-of-scope/index.htmlのようなパスにあるページは制御できません。

/scope/sw.jsが制御下に置けるのは青枠で囲んだ範囲。Service Workerスクリプトが存在するディレクトリよりも下層のページを制御下に置けるが、自身を内包するディレクトリを越えて影響を及ぼすことはできない。

Service-Worker-Allowedヘッダ

HTTPヘッダのService-Worker-Allowedフィールドに任意のパスを指定した場合には、Service Workerのスクリプトファイルよりも上位のパスにあるページの制御もできるようになります。

Service Workerが/scope/sw.jsから配信していたとしても、このレスポンスにService-Worker-Allowed: /out-of-scopeのHTTPヘッダを与えていれば、/out-of-scope/index.htmlのようなパスにあるページでも制御下に置くことができます。

ほかに知っておくべきこと

ここまでに紹介した事柄のほかにも、知っておくとよいService Workerの特徴も紹介していきます。

DOMにアクセスすることはできない

Service WorkerからWebページ側のDOMに直接アクセスはできません。DOMの操作はWebページに行ってもらいます。

状態の永続化

Service Workerは必要なときに起動するので、イベントハンドリング中になにか値を変数に保持しておいたとしても、次のイベントハンドリングの際には消えてしまいます。そのため、イベントの間で持続する情報を保持したい場合にはIndexedDBを使う必要があります。

複数のページ間でのデータのやり取りが手軽にできるものではないと思っておいたほうがよいでしょう。

まとめ

Service Workerは明確に、これまでにできなかったことが新しくできるようになるといえる技術です。

初めに述べたように、仕様自体は2年前に公開されていますが、現在でも仕様の提案が議論されています。これからも、この記事で紹介した以上に使いやすくなったり、使い方の幅は広がっていくかもしれません。

次回はコードサンプルも交えて、Service Workerのインストールの仕方や、その際の状態のハンドリングなどを紹介していく予定です。