概要
Reactアプリで本番環境に展開後、初回に限って画面が真っ白くなることがありました。
コンソールでは何かのJSファイルのトークン不正の旨のメッセージだったように記憶しています。
一度リロードすると問題なくなります。
本番環境に更新したファイルを展開したときのみ発生します。
しかもIEでは発生せずという状況です。
原因はService WorkerがJSファイルをキャッシュしていないことによるものでしたが、どうしてそういうことになってしまったのかという話です。
読者対象
- Reactアプリで本番環境に展開後の初回限定で正常表示できずに困っている(画面は真っ白、コンソールはJSファイルのトークン不正の旨)
- Service Workerを有効化している。
- CreateReactAppでビルドしている。
- IEでは問題ないのだが・・・
Road to 解決
本番環境に展開した時しか発生せず初回のみというレアパターンに難儀
本番環境に展開した時しか発生せず、初回のみというレアパターンに難儀しました。
本番環境への調査は難しいので、発生する状況を作り出すのにはしばらく時間を要した記憶があります。
Service WorkerはSSL環境でないと利用できないですが、localhostに限っては例外で利用できるため、それで調査したはずです。
JSファイルなのに<???
ネットワークの通信を見ていくとJSファイルが不正とのエラーだったと記憶しています。
JSファイルはあるし、<って意味わからんとここでも時間を要しました。
しかし、よく見てみるとmain.jsはハッシュ値をファイル名に含みますが、どうも取りに行っているのファイルと生成したmain.jsとのハッシュ値が異なることに気が付く。
更に調べていくと、取りに行っているのは前回リリースしたmain.jsファイルであることが判明。
Service Workerはキャッシュする&更新されていてもすぐに反映しない
Service Workerはmain.js等をキャッシュします。故に速い。
そして、Service Workerはサーバーのファイルが更新されていると裏で取り込みますが、それを反映するのは次回(リロード含む)です。
それが理解できていると、前回リリースしたmain.jsファイルを取りに行っているのは正常な動きであることがわかります。
(よくわかっていなかったので少し時間を使いました(;^_^A)
ではファイルが存在しないのか?という問題になります。
本番環境展開時に前回のmain.jsは削除していた
リリース作業にはかかわっていなかったので確認したところ、リリースの際に前回のmain.jsは削除していることを確認。
つまり、サーバーに前回のmain.jsがないのだからエラーになるのは当たり前という結論に。
ではファイルがないのにトークン不正とはどうして?という疑問になりますが、Service WorkerなのかExpressが404のためHTML返していた気がします。
そりゃ解読不能ですよね( ´ー`)y-~~
ちょっとおかしくないか?
結論は出たのですが、もし以下の疑問が浮かんだのなら鋭いです。
『Service Workerってキャッシュするんだから、サーバーにファイルなくともmain.js持っているはず。』
だってService Workerはオフラインにも対応できるようキャッシュが当たり前。
キャッシュしていないならオフライン動作なんてできないのだから。
これは少し古いですが、issueがあります。
https://github.com/facebook/create-react-app/issues/2612
要約すると、バンドルサイズが2MB超過するとキャッシュしないよう、CreateReactAppが仕分けしています/(^o^)\
(現在どのような動作になるのかは把握していません、あくまで当時の話です)
当時コード分割は後手に回っていて、main.jsのサイズは2MB超過していました。
そのため、この制限に引っかかっていたのです。
適切にコード分割されていればこの問題に遭遇しなくて済んだのですがorz
おまけ どうしてIEは問題なかったのか
IEはService Workerサポートしていないので、常にサーバーに問い合わせます。
最新のみ取りに来るなら、最新のmain.jsがサーバーにあるので、それは正常に描画しますよね、すごいぜIE\(^o^)/
まとめ
main.jsのサイズが大きくてService Workerにキャッシュされない悲劇の話でした。
対応策としてはコード分割してService Workerにキャッシュしてもらうのがベスト。
仮に分割できなくてどうしても無理という場合には、過去のmain.jsを置いておけば回避は可能。
ただし、クライアントはどの時点のmain.jsを要求するか不明のため、直近のmain.jsだけだと、2世代以上前のService Workerはサーバーにないファイルを取得しようとして同じことが発生するΣ( ̄ロ ̄lll)ガーン
奥の手としては時代に逆行覚悟でService Workerを利用しない・・・(;^_^A
どなたかの助けになれば幸いですm(__)m