- 作者: 久保田光則
- 出版社/メーカー: 技術評論社
- 発売日: 2017/05/26
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
本書では高速化という課題に対し、きちんと対処できる知識と実力を身に付けます。基礎となるブラウザのレンダリングから、個別の問題に対する対応例、今後を見据えた設計の基礎などその場しのぎではない本質的な高速化を学びます。
この書籍のなかでも指摘されているけど、自分は巷で話題のベストプラクティスとかを調べて満足しちゃうタイプなので体系的に学べる本書はとても勉強になった。特にチューニングの話に入る前に、ネットワークとブラウザのレンダリングの仕組みについて解説されているのがめちゃくちゃ良い。
後半のところで書かれているテクニック(特にCSS関連のもの)は、否定していた『小さな最適化』のように感じられたが、全体として基礎をきちんと抑えることができる内容だったと思う。
また少し前に話題になったdev.toの速さの解説記事を合わせて読むことで、理解が深まって楽しめた。
フロントエンドのパフォーマンスについての興味が高まってるから、次にこの2つを読んでいきたい。
超速! Webページ速度改善ガイド ── 使いやすさは「速さ」から始まる (WEB+DB PRESS plus)
- 作者: 佐藤歩,泉水翔吾
- 出版社/メーカー: 技術評論社
- 発売日: 2017/11/23
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
- 作者: Lara Callender Hogan,西脇靖紘,星野靖子
- 出版社/メーカー: オライリージャパン
- 発売日: 2016/06/25
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
読書メモ
第2章 ブラウザのレンダリングの仕組み
レンダリングエンジン
JavaScriptエンジン
- V8(Chrome/Opera/Vivaldi)
- Chakra(IE/Edge)
- SpiderMonkey(Firefox)
- Nitro(JavaScriptCore)(Safari)
Frame
- Loading
- Scripting
- Rendering
- Painting
Loading
- Scripting
- Rendering
- Painting
第3章 チューニングの基礎
- 「早すぎる最適化は諸悪の根源である」
- 「推測するな、計測せよ」
- パフォーマンス指標 RAIL
- 計測する手段
- デベロッパーツール
- Chrome DevTools
- Network パネル
- Performance パネル
- Memory パネル
- Chrome DevTools
- JavaScript API
- パフォーマンス診断ツール
- 基本的に初期読み込みのパフォーマンスを解析
- Chrome DevToolsのAudits
- PageSpeed Insights
- Lighthouse
- パフォーマンスの継続的監視
- New Relic Browser
- デベロッパーツール
第4章 リソース読み込みのチューニング
- HTML/CSS/JavaSriptを最小化する
- 適切な画像形式を選択する
- dev.to は Cloudinaly を利用
- Cloudinalyは、ブラウザごとに最適な拡張子(webp/jpeg)の選択をしてくれる
- CSSのimportを避ける
- JavaScriptの同期的な読み込みを避ける
- script要素によるJSの読み込みは、ドキュメントのパースをブロック
- defer属性
- DOMツリーが構築されてから実行
- 実行順序は宣言の順に従う
- async属性
- 実行タイミングと実行順を保証しない
- ファイルが取得された時点で実行される
- デバイスピクセル比ごとに読み込む画像を切り替える
- srcset属性
<picture>
要素やsizes
属性とも合わせて
- CSSのメディアクエリを適切に指定する
<link href="style.css" rel="stylesheet" media="screen"> <link href="print.css" rel="stylesheet" media="print"> <link href="other.css" rel="stylesheet" media="(min-width: 920px)">
- CSSスプライトを使って複数の画像をまとめる
- HTTP/2以降では意味なし
- リソースを事前読み込みしておく
- Gzip圧縮を有効にする
- CDNを用いてリソースを配信する
- ドメインシャーディング
- HTTP/2以降では意味なし
- リダイレクトしない
- URLの最後のスラッシュを付けるようにしておく
- ブラウザのキャッシュを活用する
- 強いキャッシュ(期限が切れるかキャッシュファイルが削除されるまで有効)
- Expiresヘッダー
Expires: Mon, 02 Nov 2015 13:19:30 GMT
- サーバーとクライアントの時間設定がずれてるとだめ
- Cache-Controlヘッダー
Cache-Control:max-age=600
- ExpiresとCache-Control両方が設定されていれば、Cache-Controlを優先
- モダンなブラウザではCache-Controlに対応してるから、Cache-Control設定のみでOK
- Expiresヘッダー
- 弱いキャッシュ(条件付きGETリクエスト<=>
304 Not Modified
)- Last-Modifiedヘッダー
Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
If-Modified-Since: Wed, 15 Nov 1995 04:58:08 GMT
- ETagヘッダー
ETag: "7797921b867ce392cad8415b729ffa97"
If-None-Match: "7797921b867ce392cad8415b729ffa97"
- Last-ModifiedよりETagを優先
- ETagは複数の結果をキャッシュできるのでより柔軟
- 配信サーバーが複数の場合はETag値が異ならないように注意が必要
- Last-Modifiedヘッダー
- 強いキャッシュ(期限が切れるかキャッシュファイルが削除されるまで有効)
- Service Workerの利用
- https://developers.google.com/web/fundamentals/primers/service-workers/?hl=ja
- Application Cache
- 中間者攻撃を受けて改竄されたリソースがキャッシュされ続けてしまう脆弱性のため廃止予定
- HTTP/2の利用
- QUICプロトコル
第5章 JavaScript実行のチューニング
- メモリリークを防ぐ
- Web Workersの利用
- Web WorkersのスレッドとUIスレッド間の通信には、非同期でデータを通信しあうメッセージパッシングで行う
- データは共有されずに、コピーしたものを送信する
- Transferableインターフェースを持つオブジェクトは、所有権を扱える
- データコピーする必要がなくなって、そのオーバーヘッドを回避できる
- モバイル端末でも4コアな機種が多い
- DOM要素・document/window/parentオブジェクトはWeb Workersからは使えない
- Web WorkersのスレッドとUIスレッド間の通信には、非同期でデータを通信しあうメッセージパッシングで行う
- asm.jsによるJavaScript高速化
- Mozillaが設計したJavaScriptのサブセット
- 計算はすべて静的型付け
- 計算で利用できる方はintとdoubleとfloatの3つのみ
- asm.jsモジュール内部では仮想的にGCが利用不可
- EmscriptenがC/C++のコードをasm.jsにトランスパイル
- WebAssemblyに置き換えられる流れ
- asm.jsはあくまでJavaScriptのサブセット
- WebAssemblyブラウザ上で実行可能なバイナリフォーマット
- Mozillaが設計したJavaScriptのサブセット
- SIMD.jsの利用
- 高頻度で発火するイベントの抑制
- setTimeout()やrequestAnimationFrame()で間引く
- debounce とは - slideship.com
- モバイル端末でのclickイベントの遅延をなくす
- touchendイベント後300ms待ってからclickイベントが発火
- ダブルタップかどうかを判断するため
- meta要素によるviewportを適切に設定すればOK
<meta name="viewport" content="width=device-width, user-scalable=no">
- touchendイベント後300ms待ってからclickイベントが発火
- Passive Event Listenerでスクロールのパフォーマンスを改善する
- イベントリスナがスクロールをブロックするScroll Jank問題
- {passive: true}オプション
- イベントリスナないでevent.preventDefault()が無効になることを保証
- 無駄なForced Synchronous Layoutを減らす
第6章 レイアウトツリー構築のチューニング
- レイアウトツリー構築の流れ
- 高速なCSSセレクタの記述
- BEMを用いる
- Block・Element・Modifier
- 基本的に1つのクラスセレクタのみを用いるのでパフォーマンスに優れる
- レイアウトを減らす非表示
- visibility: hiddenはペイント処理は行われないが、レイアウトツリーには含まれたまま
- display: noneはレイアウトの対象からも外すことができる
- img要素のサイズを固定する
第7章 レンダリング結果の描画のチューニング
- 再描画
- Composite Layersのみ引き起こされる場合
- opacityプロパティの値が更新
- transformプロパティの値が更新
- Composite Layersのみ引き起こされる場合
- translateZハック
.target { transform: translateZ(0); }
第8章 高度なチューニング
- バーチャルレンダリング
- なめらかなアニメーション
- 再レンダリングを減らす
- スタイル計算をスキップ
- DOMツリーの構造・DOM要素を変化させない
- レイアウトをスキップ
- translate()・scale()を使う
- ペイントをスキップ
- opacity・transformプロパティを使う
- レイヤーの合成を最適化する
- translateZハック
- スタイル計算をスキップ
- 再レンダリングを減らす
- will-changeCSSプロパティによる最適化
- レンダリングエンジン内部でどのような最適化が行われているかが把握しづらい
- あまりお勧めされてない
- CSS Containmentで再レンダリングを最適化
- レンダリングをDOMツリーに封じ込める
- 全体の再レンダリングの影響から外せる
- Google Chromeでのみ実装
.foobar { contain: content; }
第9章 認知的チューニング
- インジケータを用いる
- 1秒を超えるようであれば適用するのが望ましい
- インターフェースプレビューを用いる
- 処理が終わったように振る舞う
- 楽観的UI(Optimistic UI)と呼ばれる
- 無限スクロールを用いる
- 投機的なリソースの先読み
- Ajaxでデータを取得してアプリケーションレベルのキャッシュに保持する
- IndexDBやLocalStorageなどのストレージor単なるJavaScriptの変数で保持
- Resource Hintsを用いる
- Ajaxでデータを取得してアプリケーションレベルのキャッシュに保持する