create-react-appで生成されるservice-worker周りについてのメモ
はじめに
こんにちは。Webフロントエンドを専攻しているkqitoです。
今回はcreate-react-app
で自動で生成されるservice-worker周りについてメモします。
create-react-appする
create-react-app
のバージョンは以下の通りです。
$ create-react-app -V > 3.3.0
このような形で生成します
create-react-app test-service-worker --template typescript
今回はtypescriptでservice-workerのregister, unregisterの部分を読んでいきます。
そういえば
create-react-app repo --typescript
みたいなオプションはdeprecatedされていました。
service-worker.tsをみてみる
exportされている関数
exportされているのはunregister
とregister
の2つだけです。順を追ってコードを見てみます。
unregister
export function unregister() { if ('serviceWorker' in navigator) { navigator.serviceWorker.ready .then(registration => { registration.unregister(); }) .catch(error => { console.error(error.message); }); } }
早期returnなどはせず、navigatorグローバル変数にserviceWorkerがあるなら(ブラウザ対応してるなら)unregisterしてる単純な関数ですね。
こちらはcreate-react-app
の/src/index/tsx
にてデフォルトで呼び出されている関数です。
// If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister();
デフォルトはunregisterしてるけど、registerするときには「隠れた罠(pitfalls)」があるので注意してね的な内容ですね。
基本的に開発時にprecacheする必要はないですし、productionデプロイ時に利用するだけで良さそう。
ということは何も編集していない限りは上記のunregister
が呼び出されていると...
register
以下がregister関数の全体です。
export function register(config?: Config) { if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { // The URL constructor is available in all browsers that support SW. const publicUrl = new URL( process.env.PUBLIC_URL, window.location.href ); if (publicUrl.origin !== window.location.origin) { // Our service worker won't work if PUBLIC_URL is on a different origin // from what our page is served on. This might happen if a CDN is used to // serve assets; see https://github.com/facebook/create-react-app/issues/2374 return; } window.addEventListener('load', () => { const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; if (isLocalhost) { // This is running on localhost. Let's check if a service worker still exists or not. checkValidServiceWorker(swUrl, config); // Add some additional logging to localhost, pointing developers to the // service worker/PWA documentation. navigator.serviceWorker.ready.then(() => { console.log( 'This web app is being served cache-first by a service ' + 'worker. To learn more, visit https://bit.ly/CRA-PWA' ); }); } else { // Is not localhost. Just register service worker registerValidSW(swUrl, config); } }); } }
ifがいくつかネストしていますが、やっていることはproductionかつserviceWorkerがブラウザ対応しているときにloadイベントを登録している感じですね。
const isLocalhost = Boolean( window.location.hostname === 'localhost' || // [::1] is the IPv6 localhost address. window.location.hostname === '[::1]' || // 127.0.0.0/8 are considered localhost for IPv4. window.location.hostname.match( /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ ) );
上記を満たすホストだった場合にはservice-workerが読み込めるかを確認 + service-workerを登録するcheckValidServiceWorker
関数を実行したりしています。
service-workerでやっていること
yarn build
などで生成されるservice-worker.js
は以下のようなファイルです。このファイルが上記のservice-worker.ts
で登録されています。
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"); importScripts( "/precache-manifest.1e65759576907aec8ccc3e22bff345f4.js" ); self.addEventListener('message', (event) => { if (event.data && event.data.type === 'SKIP_WAITING') { self.skipWaiting(); } }); workbox.core.clientsClaim(); /** * The workboxSW.precacheAndRoute() method efficiently caches and responds to * requests for URLs in the manifest. * See https://goo.gl/S9QRab */ self.__precacheManifest = [].concat(self.__precacheManifest || []); workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), { blacklist: [/^\/_/,/\/[^\/?]+\.[^\/]+$/], });
これらはworkboxによって提供されているservice-workerです。
workboxについてはこちらの記事が理解しやすかったです
yarn eject
してwebpackの設定を見ればわかるのですが、workbox-webpack-plugin
というライブラリを利用してworkboxに関する設定ファイルを生成しているため生成されるわけですね。
new ManifestPlugin({ fileName: 'asset-manifest.json', publicPath: paths.publicUrlOrPath, generate: (seed, files, entrypoints) => { const manifestFiles = files.reduce((manifest, file) => { manifest[file.name] = file.path; return manifest; }, seed); const entrypointFiles = entrypoints.main.filter( fileName => !fileName.endsWith('.map') ); return { files: manifestFiles, entrypoints: entrypointFiles, }; }, }),
ざっくりまとめるとこんなことをしています。
- Precacheするファイルの指定(runtime cacheとかはしてない)
- Navigation requestsされるprecacheされているファイルの指定
こんな感じでしょうか。
ちなみに最新バージョンのworkboxではregisterNavigationRoute
はdeprecatedされている感じでした
まとめ
ざっくりと生成されるファイル等を読んでみました。
Cache Storageはパフォーマンスをかなり向上する物ですが、キャッシュ削除のタイミングなどちゃんと図らないと意図せぬ内容が表示されたりすると思うので気をつけながら利用したいところです。
以上です。