Chrome DevTools の「Enable paint flashing」を使う

Chrome DevToolsに「Enable paint flashing」という機能があって、画面で描画が行われた箇所をハイライト表示してくれます。

DevTools を立ち上げて、下のペインの「Rendering」タブを選択するとあります。下にペインがない場合、右上の「︙」から「Show console」を選ぶとペインが表示されるはずです。

f:id:kitak:20151101124447p:plain

「Enable paint flashing」は、少し前まで「Show paint rectangles」という名前だったし、今後ひょっとしたらまた名前が変わるかもしれません。名前だけでなく、DevToolsのUIはChromeのバージョンアップによって変わっていくので、上の説明はあくまで参考程度にしてください。

描画が行われた箇所がハイライトされて、なにがうれしいのかというと、不要な描画を検知し、除くことで、ブラウザのレンダリング処理を改善して、使いごこちのよいUIをユーザーに提供できることです※。

ブラウザがレンダリングの1フレームでおこなう処理は、ざっくり「Loading」「Scripting」「Rendering」「Painting」に分けられますが、極端な事例を除けば、「Painting」のコストが相対的に高いと言われています。 ブラウザは、ユーザが操作するたびに画面全てを描画するのではなく、必要な箇所のみ描画するよう、よしなにやってくれていますが、JavaScriptスタイルシートの記述によっては本来不要な描画が走ります。

いくつか不要な描画とその改善の例を紹介します。

例えば、スクロールによって追従するヘッダーやメニュー。「Enable paint flashing」を有効にして、スクロールをしたときにずっと緑色でハイライトされる箇所があれば、改善のサインです。

GPU合成を行うようにJavaScriptスタイルシートの記述を変更します( GPU合成によるスムーズなアニメーションについては、プリンより滑らか。スムーズなアニメーションの作り方 プリンより滑らか。スムーズなアニメーションの作り方。 - HTML5 Conference - - YouTube が分かりやすいです )。GPU合成が行われるようになれば、(将来的に色が変わるかもしれないですが)これまで緑色でハイライトされていた箇所がハイライトされないようになります。あるいは「Show layer borders」を有効にして、これまで緑色でハイライトされていた箇所が橙色の枠で囲まれていることを確認するとよいでしょう。

スクロールに追従するUIに限らず、スライドショーやスライダーのようなUIのアニメーションもGPU合成が行われているかチェックする価値があります。
また、これはライブラリを選定するときの基準としても有用でしょう。ライブラリのデモページで触ってみて、緑色のハイライトが目立つ場合は「うーん... 微妙」というかんじです。

次にリスト操作。リストにアイテムを追加したり、削除したり、といったことはよくあると思いますが、実装するときは、操作のたびにリスト全体を描画しないようにすることがひとつのポイントになります。アイテムを追加したり、削除するたびにリスト全体がちらつくUIはお世辞にも立派とは言えません。「Enable paint flashing」を有効にすることで、リスト全体で描画がおこなわれていないかどうかの視認が容易になります。

ちょっと前までは、この手のリスト操作はDOM職人による効率のよいDOM操作でおこなわれていましたが、 最近はAngular.js, Vue.jsのような双方向データバインディング、React, MithrilのようなVirtualDOMの概念を持つフレームワーク・ライブラリによって、不要になりつつあります。
これらのフレームワーク・ライブラリは互いに概念や表面的なAPIが異なっているかもしれませんが、DOM操作をフレームワーク・ライブラリに委ねる、という点では共通です(どの程度委ねるかは違いますが)。
リストに関する、どのアプリケーションでも登場するDOM操作は、よしなにやってくれます。
とはいえ、丸投げしてよいかというとそうでもなく、これらのフレームワーク・ライブラリを使ったことのある人は知っていると思いますが、idやkeyのようなリストアイテムを一意に識別できるものを割り振る必要があります。これはフレームワーク・ライブラリが効率のよいリスト操作を実現するために必要なヒントです。これがないと操作のたびにリスト全体で描画が行われることもあるので、ただ適当にフレームワークやライブラリを使えば良い感じになるというわけでもありません。

最後に双方向データバインディングに関連した話。
Angular.jsやVue.jsのような双方向データバインディングフレームワークを使っていると、データバインディングのチェインによって、ある値の変更が意図していない場所に伝播し、不要な再描画が発生するかもしれません。「Enable paint flashing」を有効にして、操作することで不要な描画が行われている箇所を特定することができます。 こういった場合は、コンポーネントの設計やデータバインディングの範囲を見なおすといいかもしれません。

いくつか不要な描画とその改善の例を紹介してきました。 極端に神経質になる必要はないと思いますが、一通りコーディングを終えた後に触ってみて、表示や操作がカクつく違和感を感じたり、モバイル向けのウェブサイト・アプリケーションを開発していて可能な限りパフォーマンスを向上したい、という場合は不要な描画をみつけてレンダリングの改善を図ってみたらどうでしょうか。

※ PaintingではなくLoading, Scripting, Renderingにボトルネックがあるケースもあるし、その場合はNetwork, TimelineやProfilesで原因を特定して改善を図ります。