2024.10.10
将来は卵1パックの価格が2倍に? 多くの日本人が知らない世界の新潮流、「動物福祉」とは
リンクをコピー
記事をブックマーク
宍戸俊哉氏(以下、宍戸):フロントエンドの話をしていきます。「高速化のためにフロントエンドですべきこと」を4つあげさせてもらいました。「あとでいいことはあとでやる」「必要なことは先にやる」「使い回せるものは使いまわす」「クライアントにとって最適なものを配信する」。だいたいこの辺の原則に則って今のフロントエンドの高速化のプラクティスってあるのかなと思っています。
「あとでいいことはあとでやる」というところで、1つはクリティカルレンダリングパスですね。これを減らしていくというところですね。
クリティカルレンダリングパスはもう説明いらないかなと思うんですけど、CSS、JavaScriptのファイルが読み込みにいくときにレンダーブロッキングを発生させているので、なるべく非同期化したり、インラインしたりしようというところですね。
JavaScriptは基本的に全部非同期で読み込むようにしています。普通にasync/deferをつけてscriptタグから読み込むという方針でおおむねよいと思うんですけど、r.nikkei.comの場合はpolyfill serviceというライブラリを使っています。このライブラリはブラウザ間によってはまだ実装されていないAPIなどの差分を吸収してくれるサービスです。
こいつを使っているのでほかのコードというのは全部polyfillが存在する前提で書いているんですね。なので、polyfillをロードしてからほかのスクリプトを実行するような書き方をしています。DOMContentLoadedは一切使わずに、ほとんどのものがpolyfillのonloadイベントをキャプチャして初期化するようになっています。
CSSはどうかというと、Critical CSSという概念があると思うんですけど、一番最初に画面に映るスタイル以外は全部あとから読み込もうというようなアプローチですね。
これやるとCSSのクリティカルレンダリングパスをなくすことができるんですけど、r.nikkei.comでは、いろいろ試行錯誤はしたんですけど、まだ入っていないです。なんでかというと、会員種別によって画面が違うだとか、レスポンシブ対応が必要だとか、そういった要素が入ってきてまだうまく行ってないですね。
ライブラリとしては自動生成するものが、npmの「critical」というやつと、あとはGoogleのPageSpeed ModuleというのがNginxのプラグインを出していて、そいつが動的にCritical CSSをインラインに埋め込むというようなライブラリがあったりします。
たぶんこれでも、いろいろトライした結果、「最終的にはマニュアルで全部メンテしていく勇気と覚悟が必要そう」という結論に今はいたっています。
次はセクションの遅延ロードですね。画像は遅延ロードするのはみんなやっているかなと思うんですけど、r.nikkei.comだとセクションごとに遅延ロードを行ったりしています。
「コンテンツのサイズ」というのはDOMノードの大きさとか深さですね。これはパフォーマンスにすごい大きく影響してきて、モバイルだとさらに顕著だったりします。
今のPCの日経電子版ってトップページだけだと60記事以上リンクがあって、ビジネスサイド的には全部出してほしいというような要望があったので対応しました。最上部除いてセクションごと全部遅延読み込みさせるような仕組みで、初回レンダリングのコストを下げる工夫をしています。
セクションのLazy Loadingをやるために「スケルトンスクリーン」というのを実装していました。これはなにかというと、Intersection Observerを2回に分けて非同期で読み込むような処理ですね。
最初に長めのthresholdでIntersection ObserverでスケルトンだけのセクションをDOMに追加します。そのあとスクロールが進んできたら実体で入れ替える。
そうすることで、このIntersection Observerの長めの1回目の読み込みでこのスケルトンスクリーンに出しているDOMサイズを減らす処理をしています。これによってリフローもあんまり起きないかなという感じですね。
それと「必要なことは先にやる」というところで、HTTP/2のServer Pushですね。画面の表示に必要になるリソースというのはなるべく速く読み込むようにする必要があって、これもFastlyのHTTP/2の機能を有効化して実現しています。
Curlでちょっと見づらいんですけど、Curlでr.nikkei.comのトップページを叩いてみると、中は死ぬほどLinkヘッダが入っているんですね。
効率最適化できているかというと自信ないんですけど、トップページの表示に必要になるリソースを基本的にリンクヘッダに一緒につけてあげて、ブラウザのPush Cacheの中に入れておく。そうすると、index.htmlを読み込んだあとに、例えばCSS取りに行ったりJS取りに行ったりでネットワークリクエストを発生させずに返すことができる仕組みになっています。
次はResource Hintsですね。日経電子版だと、朝夕刊といってその日の紙の新聞とまったく同じ構成の記事を表示するページがあります。
ほとんどユースケースとしては、毎日紙の新聞を読む人が1面からなんとか面までずっとガーって読んでいくのと同じように、各面をバッと舐めるように閲覧するユーザーが多いんですね。次のページはResource Hintsでキャッシュしてしまおうという処理ですね。これはprefetchというものを使っています。
prerenderとdns-prefetchというのも使っていて。prerenderは検索への導線ですね。マウスカーソルがアイコンに近づいたらリンクタグを動的に挿入します。あとは広告のサードパーティドメインに対してdns-prefetchをするなどして先読みを行っています。
あとはService Workerを使った事前キャッシュですね。トップページやその画面のページからPostMessageを投げて、Service Workerの中でAPIから記事の一覧を取りに行って、その返ってきた記事の一覧から実際に記事のオブジェクトを取り出すというような処理ですね。その返ってきたオブジェクトをService WorkerのCache APIの中に入れてあげる感じですね。
だいたいこれを使うと記事のページというのは20〜30msぐらいで返せるようになっています。あとはオフラインで使えるようになるなどいろいろな恩恵があります。
サブリソースは、先ほどのService Workerのレスポンスからだと記事のページのレスポンスは取れるんですけど、必要になるJSとかCSSとかそういうサブリソースが取れないんですね。
それはService Workerの中でレスポンスヘッダをパースしてあげて必要なリンク、必要なサブリソースのURL一覧を取ってきて、それをService WorkerのaddAllを使ってキャッシュすることで実現しています。
キャッシュ同期の問題というのがありまして、ログインしたとき、あとは非ログインのとき、古いキャッシュがどうしても残ってしまうというのがあります。有料会員のはずなのに「この記事は有料会員限定です」という画面が表示されてしまいました。
古いキャッシュがストレージに残っているというのも問題というところで、それにどう対応してるかというと、ログイン・ログアウトのURLをService Worker内でキャプチャして対応しています。そのログイン・ログアウトのURLをキャプチャして該当のURLが来たらキャッシュ削除する処理ですね。
もう1つはpostMessageですね。Service Worker内、Service Workerから返されたレスポンスに含まれるユーザーの会員属性、それとCookie内に保持している最新の会員属性。それが違えばクライアントからService Workerにメッセージを投げてキャッシュ削除するという形ですね。
古いキャッシュの削除どうやってやっているかというと、キャッシュしたURLとタイムスタンプをIndexedDBに入れています。そのIndexedDBに入れたデータとCache Storage内のリソースを同期させることで実現しています。
だいたいこんな感じですね。
BackgroundSyncを使ってService Workerにキャッシュ要求を投げて、Service Workerは新しいキャッシュ要求を行う前にIndexedDBを参照して、そして古いキャッシュが残っていればそれを削除するといった処理ですね。
「使いまわせるものは使いまわす」は一般的な話なんですけど、静的ファイルにはハッシュ値をつけて長時間キャッシュするようにしています。リロードの対策としてimmutableをつけてあげると、リロード時もキャッシュの再読み込みがなくてよいなという感じですね。
デザインシステムですね。UIコンポーネント。「NIKKEI UI」というコンポーネントベースのスタイルガイドを作成していて、それをr.nikkei.comでは使うようにしています。
どういうメリットがあるかというと、マークアップの開発コスト削減やスタイル定義が重複しないように開発ができます。UIに一貫性が出ることと、アクセシビリティ、このあたりを担保できます。
いろいろキャッシュの話をしてきたかなと思うんですけど、あなたのキャッシュはどこから? 私は鼻から……。あ、これ言いたかっただけです(笑)。
(会場笑)
どこからキャッシュしたのかけっこうわからなくなるんですけど、これは現状アクセスログ、Chrome Dev ToolsのNetworkパネルのInitiator、あとはsizeの部分から判断するしかないかなと思っています。あとはApplicationタグを使うとService Workerの設定でBypass for networkというチェック項目があるので、それを使ってService Workerを無効化する。
このあたりはサイト高速化の教科書とも言っていい『超速! Webページ速度改善ガイド』、これを読むといいかなと思っています。
「最適なリソースを配信する」というところでpictureタグですね。pictureタグを使ってクライアントの画面幅に合わせて最適な大きさの画像を設定しています。
これはHandlebarsのヘルパーを使って実装しています。
これつい先週のやつなんですけど、画像配信を少し見直したんですね。今までjpeg画像のクオリティはけっこう高めに設定してたんですけど、見た目に大きな変更のない範囲で、見た目に大きな劣化のない範囲で変更して、だいたいコンテンツのサイズが半分ぐらい改善することができました。
Performance Budgetを定義しておくと、定期的にBudgetオーバーしてるから改善しなきゃねというきっかけになるので便利です。
「Network Information APIが使えるよ」というところで、通信の種別環境の種類を取得できるAPIなので、Wi-Fiかcellularなのか、3Gなのか4Gなのか、Round-Trip Timeなのか、こういったものが取得可能になります。
Service Workerで大きめなリソースをガッとキャッシュすることがあるんですけど、そういうのはWi-Fi接続時のみに限定したりしています。
最後まとめになります。高速化は一番根本的なUX改善になるのかなと思っています。
まずは分析です。Lighthouseを使ったりwebpagetestを使って分析をしていきましょう。SpeedCurveなどを使って継続的にモニタリングできる仕組みを作ると、一気にパフォーマンス改善というのはやりやすくなります。フロンドエンドだけじゃなくてCDNやサーバサイドのパフォーマンスというのも重要になっています。
あとはキャッシュの効率化、クリティカルレンダリングパスの削減、そういう手がつけられるわかりやすいところから始めるといいのかなと思ってます。さっきのこの画像と同じように、パフォーマンスの改善というのは定期的にやっていくとよいです。そのときにBudgetを定義しておくとやりやすいです。
速くすることを考えるよりも、遅くなる要素を減らしていくことが一番大事かなと思っています。
以上です。ありがとうございました。
(会場拍手)
関連タグ:
2024.11.13
週3日働いて年収2,000万稼ぐ元印刷屋のおじさん 好きなことだけして楽に稼ぐ3つのパターン
2024.11.11
自分の「本質的な才能」が見つかる一番簡単な質問 他者から「すごい」と思われても意外と気づかないのが才能
2024.11.13
“退職者が出た時の会社の対応”を従業員は見ている 離職防止策の前に見つめ直したい、部下との向き合い方
2024.11.12
自分の人生にプラスに働く「イライラ」は才能 自分の強みや才能につながる“良いイライラ”を見分けるポイント
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.11.11
気づいたら借金、倒産して身ぐるみを剥がされる経営者 起業に「立派な動機」を求められる恐ろしさ
2024.11.11
「退職代行」を使われた管理職の本音と葛藤 メディアで話題、利用者が右肩上がり…企業が置かれている現状とは
2024.11.18
20名の会社でGoogleの採用を真似するのはもったいない 人手不足の時代における「脱能力主義」のヒント
2024.11.12
先週まで元気だったのに、突然辞める「びっくり退職」 退職代行サービスの影響も?上司と部下の“すれ違い”が起きる原因
2024.11.14
よってたかってハイリスクのビジネスモデルに仕立て上げるステークホルダー 「社会的理由」が求められる時代の起業戦略