2018の第3日目の記事です。はじめにGCPの無料枠が終了したため、ポートフォリオサイトはクローズドしましたが、引き続き本記事はご覧いただけます。私は以前、インターンシップなどの選考に備えて自分のポートフォリオサイトなるものを作りました。あまりに速くデプロイしたかったことからパフォーマンスについて一切無視した結果、とてつもなく遅いサイトができてしまいました。結局、他人に見せるには恥ずかしいサイトになってしまい、今回の記事で爆速にできればと思います。サイトはこちらで、SEO対策やOGPも実装したので以下のように展開されます。DevToolを使い倒すとよくわかる。Lighthouseは便利だし、解決策をくれるいろんなサイトをみても最初は画像の質が悪い(サイズが大きいなど)の問題が目立つので注意するサイトが遅くなる主な原因ネットワークに関することです。ブラウザでコンテンツをリクエストすると、ネットワークを介してレスポンスが返ってきます。通信速度などの影響を受けるので、速度計測ツールなどによって検証が必要です。レンダリングに関することです。画像などのデータはラスタライズされて表示されますが、一例として、画像が非常に大きいと、その時間もかかってしまいます。リッチなアニメーションを導入していると、ボトルネックになりがちです。レンダリングの効率について検証する必要があります。スクリプトに関することです。これは主にJavascriptを指します。ロジックについては注意が必要です。使用メモリなどを検証する必要があります。実際に遅いのかを定量的に調べるまず、自分のサイトがどのくらいの時間でレスポンスを返すのかを調べます。実際にどのくらい遅いのかを数字で知ることが重要です。Chromeの「デベロッパーコンソール」のNetworkタブで調べることができます。Canaryを使っているので、興味がある人は使ってみてください。Cacheにチェックボックスをいれます。すると計測結果が下に表示されます。そもそも遅くても1.0秒が閲覧者のUXにもいいということなので、至急改善しないといけません。全体的なサイトの評価は、"Audit"タブで測定することができます。20秒ぐらいかかりました。最初にモバイル端末から調査しました。パフォーマンスが非常に悪いです。タグを適切に使うなど、SEO対策はしたつもりだったので、かなりスコアはいいです。提案は以下の通りです。ネットワークのペイロードに問題があるみたいです。デスクトップも調査してみると同様の結果です。これからネットワークに関してレンダリングに関してスクリプトに関しての3つのセクションで解説していこうと思います。ネットワークに関してまず、ネットワーク処理の3原則は以下の通りです。データの転送量は小さくデータの転送回数は少なくデータの転送距離は短くまたブラウザの内部構造を知る中でもクリティカルレンダリングパスは非常に重要です。クリティカルレンダリングパスクリティカルレンダリングパスでは最初にHTMLをダウンロードして、バイトを解析し、トークンに変換し、DOMツリーというものを構築します。...のようなstyleがあるとそれをネットワークから取得してCSSOMツリーを構築します。最終的にはDOMツリーとCSSOMツリーを一つにレンダリングするわけです。これでできあがったものをレンダリングツリーと呼びます。レンダリングツリーは構造が知れても、端末内でどのように表示するかはわかりません。windowなどがわからないということです。次のレイアウト段階で決まります。このレンダリングツリーを実際のピクセルに変換することをペインティングやラスタライジングと呼びます。ここで見えたDomContentLoadedは青線で表示され、DOMツリーが構築された状態も示します。onloadは全てのリソースの処理が終了するまでの時間を示していて、赤線です。後から参考画像を追加したのですでに爆速化し終わったものですが、このような対応関係です。幸いにもリソースによってそこまで大幅にレンダリングがブロックされてはいませんでした。今回はなかったのですが青線と赤線の差が短いのならば何かしらブロッキングされているということですので、注意してみてください。ここで少し体系的にクリティカルレンダリングパスを説明します。仮に以下のようなレンダリングパスをとるページがあるとしましょう。HTMLだけのページです。このときはT0とT1はネットワークから取得するまでの時間なので、あまりアプリケーションエンジニアはすることはありません。厳密に言えばデータの転送距離は短くの原則から近くのリージョンのデータセンターにコンテンツを置いたりすることで高速化できます。次に新たにCSSを追加したとします。HTMLとCSSで二回取得が必要になります。:初回のページレンダリングに必要なバイト数です。デフォルトではHTMLもCSSもクリティカルリソースです。HTMLがダウンロードされるまで何も見えないからです。CSSも同様で、CSSOMが構築されるまでは処理済みコンテンツのレンダリングを保留します。できるだけ小さくして、できるだけ早くクライアントに配信しましょうここでまたjavascriptファイルがあったとしましょう。クリティカルリソースであるjsファイルはJavascriptを実行するためにブロックしてCSSOMを待つ必要があります。scriptタグにasyncを追加するとになり非同期スクリプトは、パーサーブロックをしないためクリティカルリソースではなくなります。CSSも特定のページしか使わないのならば以下のようにレンダリングをブロックすることもないでしょう。これはCSSにもいえて、例えばモーダルが使われていて、それに使うCSSならモーダルが出された時にロードする方が初期ページレンダリングをはやくすることができます。クリティカルレンダリングパスを知ることで、ユーザーにすばやくコンテンツを返すことができるようになると思います。転送はなるべくはやくなるように距離を短くデータセンターに近くなるようにするなど工夫をする必要なスクリプトは優先的に届けたり、必要性の低いものは別のjsファイルにしたりすることもできます。実際にネットワークを調べるここでは、Networkタブで、「どのような通信があり」、「どのくらい時間がかかったか」を調べます。Waterfallです。説明は不要かと思いますが、Waterfallとはリクエストのダウンロードの処理の詳細と、かかった時間のことです。このサイトだと、以下のような画像が非常に遅いことがわかります。遅い画像は明らかにデータサイズが大きいので、データサイズを改善する必要があります。あとから気づいたのですが、時間でソートして表示すればよかったです、すみません。各々の詳細は項目を選択しTimingタブを見ることで設定できます。TTFBは最初の1バイトがくるまでの時間を表しています。6msとわりかし高速であり、フロントエンドエンジニアが何かできることはありません。TTFBが遅いと通信状態が悪かったり、バックエンドに問題があるのではないかと予想することができます。サーバーサイド、インフラの経験からクラウド環境のネットワークを考えることもできるとは思いますが、、、今回の記事ではそっとしておきます。Downloadはコンテンツ(画像なりファイルなり)をダウンロードするまでの時間ですコンテンツのサイズが非常に大きいのでダウンロードに時間がかかっているのでしょう。これは、レンダリングのところで再度指摘します。通信のオーバーロードがかかっているわけではないので、ネットワーク処理としては大丈夫そうです。画像の遅延ダウンロードする不要なライブラリどを削除するなどができると思います。今回は遅延ダウンロードをするためにvue-lazyloadというものを使いました。github.comコンポーネットごとまるごとlazyload特定のタグをlazyloadを簡単にできるので採用しましたが、かなり使いやすかったです。通信状態の悪い状態で試してみる通信制限や、単純に電波が届きにくい場所にいるなど通信状態が悪い時はいくらでもありえます。そのようなときにDevToolではシミュレーションをして、どのようなUXになるのかをあらかじめ知ることができます。DOMContentLoadに7.63秒で、Loadには28.48秒なので、わりかしはやくに何かしら表示できているのではないでしょうか。レンダリング処理に関してレンダリングの原則は以下の通りです。1フレーム内の処理を軽くすること。60FPS以上が理想的ブラウザの内部処理を利用して最適化を測ること特に"Performance"で知ることができるので使ってみます。Recordを押して記録をはじめ、適当にスクロールなどをしたら停止しましょう。8秒ほど録画しました。アニメーションが皆無なので特に問題そうなところはありません。大事な箇所を解説をします。ここでもネットワーク処理について知ることができます。スクロールなどをして画像が読みこまれるなど、ネットワーク処理が期待される場合に、調べることができます。全体として、どのくらいスクリプト処理、ペイント処理、レンダリングに費やしているかを知ることができます。DOMツリーとCSSOMツリーを合わせることペイント処理で特に困ることといえば、CSSなどでbox-shadowなどの比較的に重いグラフィカルな処理のことです。一般的には問題になることはありませんがあまりにも多用しすぎると重くなるので注意が必要です。レンダリング処理が重いです。ペイント処理に関してここだけ、副セクションを定義して調査したいと思います。繰り返しになりますが、ペイント処理とはユーザーの画面に合成されるピクセルを書き込む処理のことです。背景、テキスト色、影などの非形状プロパティの変更をしたときなどでレイアウトがトリガーされたときなどにペイント処理が発生します。前者(要素の変更)はレイアウトをトリガーしています。後者はレイアウトはトリガーされませんが、以下のようなパイプラインが実行されます。レイアウト算出と同様に包括的な計測には"Performance"が参考になります。instrumentation"をチェックしてください。以下が測定した結果です。これによって具体的なCSSの何が遅いのかを知ることはできません。遅いところを調査することになります。スクリプト処理に関してスクリプト処理の原則は以下の通りです。UIブロッキングをするような処理を避けるメモリリークを検出し、節約するようにするjavascriptはメインスレッドで実行するので、その間はレンダリング処理をすることはできないので、その間は塞がれることになります。メモリリークを見つけるメモリリークの調査をします。ヒープ領域の調査は"Memory"タブで計測することができます。ヒープとは、動的に確保されるメモリ領域で、例えば以下のようにnewするとヒープアロケートがおきます。Date()そして、スコープを抜けるとガーベジコレクションによってメモリ解放されます。}しかし、何かしらの問題でメモリが解放されずに漏れるメモリリークというものがあります。メモリリークの調査方法は以下の動画で知りました。www.youtube.comなぜメモリリークを解決するのが大事というとメモリリークするとスクリプト処理などのパフォーマンスが悪くなる画面がかくつくなどいろんな問題があります。"Memory"タブではヒープアロケーションとヒープスナップショットをとることができます。重い処理は再度"Performance"で調べることができます。ヒープ(領域)とはプログラムが一時的に使用するメモリのことで、OSなどによって割り当てられ、主に関数間で変数などを使いまわせるようにするためにヒープを使います。ここで以下のように選択します。実際に録画して、何かメモリリークしそうなところをぽちぽち押したりして、ヒープを確認します。青い線がヒープアロケーションで、メモリが割り当てられています。青い線がありますが、イベントごとにlistenerを解放しているので青い線はすぐ灰色の線に変わるので問題はありません。実際にメモリリークがあれば、スナップショットを取れば解決できます。最初にページをロードしたときにスナップショットを取ります。気になるところ(タブを何回も開いたりして)再度、スナップショットをとります。SummeryではなくComparisionを選択しましょう。すると二つのスナップショットで比較をすることができます。私はないのですが、赤で表示される項目は参照がなく、メモリリークをしている状態です。Swiftだとweakなど弱参照をつけて避けていたようなことが実際に起きているのでバグを直す必要があります。一例としては、よくやる間違いですがAという要素のイベントリスナーBを定義するAが消されるBが取り残されるのケースがありえます。画像を圧縮するVue.jsでは、プロダクション環境ではすでに圧縮されていますが、元の画像を圧縮するといいでしょう。ChromeのAuditを使うとWebPを推奨されますので、<picture>でWebP非対応のブラウザも対応します。developers.google.comWebPとはPNGより26%小さく25~34%ほどJPEGよりも小さく同等の画質の静止画フォーマットです。対応状況は以下の通りです。[画質]と指定します。[画質]はデフォルトが75のようで、小さいほど荒くなってしまいますが、画像を小さくできます。実際にHTMLで記述するときは<picture>で囲ってください。<picture>とは0個以上の<source>1個以上の<img>を含むもので画面や端末にあった画像を提供できる要素です。<source>の中から適切なものがあればそれを選び、なければ<img>を使います。img(src="hogehoge.png")のように使います。画像だけNetworkタブでみたいときはFilterでImgを指定すると見ることができます。元の画像の次元数を減らさなければなりませんので、すべて約50%縮小しました。再度、AuditとPerformanceによる評価をします。ここではモバイルで検証します。ポイントが時間に対して線形ではないのですが、特にいいスコアを出していますね。ネットワークの方は、画像群がかなりボトルネックになっていますね。遅延ロードを実装する次にコンテンツが下のものは遅延ローディングを実装します。以下のように実装できます。alt="portfolio")まずは画像ファイルから。読み込まれていく様子がわかります。今回速度を計測すると、たまに1.2秒を下回るくらいです。同様にはてなブログの記事も、遅延ローディングしました。はてなブログのリンクも重かったためです。ここでキャッシュを使わずに、約0.8秒を実現しました。Storageに保存しました。無料枠をすべて使ってしまっていたので同じファイルに保存していました。Auditスコアはこちらです。再度、圧縮する27インチのiMacの全画面でみてみて、許容できるまで再度圧縮しました。一回するかというと許容できる粗さまでに圧縮したいと思ったからですし、伸び代(高速化)の一つです。キャッシュを使ってですが、0.2秒くらいで表示できるようになりました。キャッシュを使わないと0.6秒くらいです。肥大化しているJSファイルを見つけて縮小させるWebpackとは、先ほどのも説明したようにバンドルファイルにまとめてくれます。ファイルをまとめるといってもまとめ方がおかしい使わないものが混じっているなどいろんな可能性があります。逆に必要なものをレンポンスに含めなれなかったり、取得が遅かったり問題を起こす反面もあります。まとめることによってHTTPリクエストを減らしたら、ファイルの大きさを小さくできることができ、本記事での目的である「爆速化」に貢献するのは間違いありません。副次的なものとしてはデバッグしやすかったり、管理しやすかったりします。以下のようにdist以下はなっています。使って可視化をして、一体何が大きいのかを探る必要があります。--reportするとlocalhostで以下のようなページが立ち上がります。支配的な項目から縮小させていきます。前提として、濃淡によって依存関係が階層構造になっています。明らかにat-uiが大きすぎます。参考にat-uiが何であるかというと軽量なUIライブラリです。O2Team使用箇所は以下の自己紹介部分です。ファイルの大きさは約330KBで、gzipでも35KBくらい食っているので、できるならば自分でCSSなどで直接書いて置き換えます。試しにローカルホストで起動します。取り除いていないので以下のようになっています。at-uiをプロジェクトから取り除きました。すると以下のように1.1MBになっていて約400MB分だけ縮小しました。再度、可視化をすると以下のようになっています。これから支配的なのはVue自体ですが、これは私自身では改善が望めないのでここでやめました。転送速度が約100ミリ秒変わったので、大きさ一歩ではないでしょうか。デプロイ環境では以下のようになっています。まとめ今ではこんな感じですね。今回は爆速化ということでスピードにフォーカスしたのでPWAやオフラインでの閲覧などは考量してないです。まとめとしてはネットワーク処理は、基本的にhttp通信に関することブログうめこみなど、ボトルネックがある可能性があるサーバーサイドなどの経験があれば、イベントリスナーなどくらいしかスクリプト処理は問題にならないはずといれど、forなどがあれば計算量を想定すべきレンダリング処理は、特に画像を正しく用意しないとボトルネックになりがち数MB単位の画像を使っているサイトもあったり、以外に対策法は多様である。速度は大事ですが、それはUXが何より大事だからです。1秒以上待たされるけど、ローディングアニメやプログレスバーがリッチだったら、逆に待ち時間は短く感じるかもしれません。そっちの方面で勉強しないといけないと痛感しました。web-grpcなどやってみたいことがたくさんありますが、卒業のために卒論に集中します。ご覧いただき、ありがとうございました。CIというものがあります。Scoreを知ることができます。エンジニアとしてはフィードバックを常時もらえるようになるのでぜひ設定してみてください。»プロフィール学生エンジニア。忘れてしまうので備忘録です。flaskでServerlessにホストするTelepresence概論スターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。