Mafiaこの記事ではブラウザの仕組みを図解を用いてわかりやすくご説明します。最近のブラウザは優秀なので、ブラウザの仕組みを理解していなくても、パフォーマンスの問題が発生することは少ないかもしれません。アニメーションを多用するサイトやユーザーインタラクティブなサイトの場合、パフォーマンスの問題はとてもシビアです。ブラウザの仕組みを知ることで、ブラウザのパフォーマンスを最大限に引き出す実装を行うことができます。画面がなめらかに表示されないのはなぜ?スクロールをしていてカクつく。アニメーションがカクカクしている時というのはブラウザがどういう状態なのでしょうか?まずは、この状態を定量的に説明するためFPS(フレームレート)から説明します。Secondの略で1秒ごとの画面(フレーム)の切り替わる回数を表しています。ブラウザでサイトを見た際には最高で60fpsとなるため、1秒間に60回画面が更新されていることになります。スクロールによる表示の更新やアニメーションはテレビと同じ様に静止画を高速で切り替えることで動いているように見せているわけです。一般のテレビのフレームレートは30fpsです。最近出てきた4k、8kテレビは60fpsなので60fpsあれば人の目にはキレイに写るということがわかります。1/60)を行っていることがわかります。Javascriptの処理時間や画面描写処理に重い処理があると、画面更新のタイミングまでに処理が間に合わず、フレームレートを下げてしまいます。逆に、16ms以内に処理が完了すれば、画面がちらつくことも、カクカクすることもないわけです。(厳密には例外もありますが。ブラウザで画面を見ていてパフォーマンスが悪いなと感じた場合、16ms以内に処理が完了していないということなので、重い処理がないか、または頻繁に呼ばれている処理がないかなどを見ていくことになります。ブラウザは各フレーム毎にどのような処理を行っているのでしょうか?もう少し具体的に見ていきましょう。下の図はブラウザが各フレームごとに行っている処理の流れです。Composite(コンポジット)の流れで画面にどの様に表示すればよいのかを計算しています。画面の初期表示の際にはブラウザはこの処理フローを最初から行います。逆に画面の表示内容に更新があった場合は必要な工程のみを処理し、再計算が不要な工程は省略します。いうことは、どの変更がどのフローに関連しているのかを知っているだけで、ブラウザの負担を大きく減らすことができます。これがフロントエンジニアがブラウザの仕組みを知る最大のメリットです。ポイント上で挙げた処理フローにはユーザーが独自で追加したJavascriptの実行時間は入っていません。ここに挙げたフローに加えてJavasriptの実行時間が毎フレームで16ms以内であれば、パフォーマンスの良い実装が行われているということができます。それではそれぞれの工程を詳しく見ていきましょう。Parse(パース)この工程ではHTMLとCSSの解析が行われます。Rules(スタイルルールズ)という処理しやすい構造体に変換します。これらの構造体を生成するのがパースの役割です。Treeの作成HMTLをパースしてDOMツリーを作成します。DOMツリーの一番上はdocumentになります。Rulesを作成します。余談ブラウザのレンダリングを担当する機能をレンダリングエンジンといいます。SafariはWebkit、ChromeはBlink、FirefoxはGecko、EdgeはEdgeHTMLというレンダリングエンジンが使用されています。それぞれ少しずつ処理フローが異なるため、ブラウザによる挙動の違いが生まれます。Rulesの紐づけが行われます。具体的にはどのスタイルがどの要素に適用されるのかをマッチングし、複数個のスタイルが一致する要素に関しては、スタイル適用の優先順位に従って最終的に適用されるスタイルを割り出します。Tree(レンダーツリー)と呼ばれます。Treeの各要素はRenderer(レンダラー)と呼ばれます。Treeにより、どの要素にどのスタイルが適用されるのかをブラウザは確認できるようになりました。この工程ではそれぞれの要素の位置と大きさの計算を行います。この処理はLayout(レイアウト)、またはReflow(リフロー)と呼ばれます。Treeの上流から下流にかけて再帰的に行われます。画面の初期表示の際は一番上位のHTMLタグに相当するルートのレンダラーから計算が始まり、各階層を順番に処理します。逆に画面更新をした場合(例えば、Javascriptで要素を挿入した場合や位置情報を書き換えた場合)はブラウザでは再計算の範囲を最小限で済ませたいため、その要素と小要素、同じ階層の兄弟要素の再計算が行われます。Tree(レイアウトツリー)が作成されます。Treeは画面に表示する対象のものだけを含みます。noneを適用した要素はLayoutツリー内には登録されません。hiddenを使用します。'Text'}などの疑似要素はこの工程でLayoutの対象として含まれます。Layout(インクリメンタルレイアウト)の2種類があります。Layoutはページ全体に関わるレイアウト変更が行われた際に発生します。ブラウザの幅が変更された場合やページのフォントサイズが変更された場合です。Layoutは変更が限定的な場合(要素が追加された場合など)に実行されるため、比較的軽い処理となります。Layoutの工程を経て、要素を出力する位置や大きさ、色がわかりました。いや、少し待ってください。画面上では要素が重なっている部分があります。どちらを先に描くかによって見え方が全然違います。Paintの工程では要素の重なりを考慮して、順番に命令を作成することで正しく描写できるようにします。Records(ペイントレコーズ)と呼ばれます。Contexts(スタッキングコンテキスト)』を元に決定されます。キングコンテキストは要素の出力順を保持しています。Tree(レイヤーツリー)を作成します。Layerの分離は変更の際の計算量を少なくするために大変有効です。Layerを分離せず描写を行った場合には、変更があった際に他の要素との影響を考慮して多くの計算を行う必要があります。レイヤーを分離して他の要素を考慮しないでよい状態にしておくと、そのレイヤーのみ再計算を行えばよいことになり、計算量を大幅に削減することができます。Treeのレイヤーを分離する条件は先程のz-indexとは関係がありません。translateZ(0))やtransformを使用したアニメーションを検知した際にブラウザはLayerを分離します。will-changeを使用したアニメーションプロパティーの指定が合った場合にもLayerは分けられます。レイヤーの状態はChromeの開発ツールのLayerパネルで確認する事ができます。Thread(メインスレッド)上でおこなわれます。Threadは開放されます。Threadが働きます。Recordsを元に各レイヤーのピクセル単位で色を当て込んでいきます。レイヤーの大きさはそれぞれですが、場合によってはページ全体に及ぶため、非常に計算量が多くなり、一つのスレッドで行うと時間がかかってしまいます。Thread(ラスタースレッド)に代行されます。Threadから依頼された分を並行して処理します。Threadは集約し、合成レイヤーを作成します。この合成レイヤーをGPUに送り画面に表示します。合成レイヤーの作成はViewport(画面内の領域)から優先して行い、スクロールなどの画面処理を受け取った際に順次作成され、GPUに送られます。詳しくアニメーションによく利用されるtransform、opacityの適用もこの工程で行われます。ThreadでJavascriptが実行されている場合でも、処理の完了を待つ必要がなくパフォーマンス低下を防ぐことができます。leftなどを使用したアニメーションではLayoutの工程から計算し直す必要があるため、パフォーマンスの低下が発生します。これでレンダリングの工程はすべて完了です。次の段落では各工程とスレッドの関係について説明します。レンダリングとスレッド下図のようにレンダリングの工程によってブラウザは異なるスレッドを使用します。Threadはその名の通りメインで使用されるスレッドです。ThreadはParse〜Paintまでを基本的に担当しています。Threadではレンダリングの処理以外にもJavascriptの実行を担当しているため、常に大忙しです。Threadの負担をなるべく軽くしてあげる事が重要です。Threadはレイヤーの合成が担当です。Threadにレイヤーのラスタライズ(ビットマップ化)を依頼します。Threadによって一枚のレイヤーに合成されます。ThreadがComposite処理を一時的に代行します。Threadレイヤーのラスタライズ(ビットマップ化)を行います。Threadから依頼が来ます。GPU画面への描写を行います。ThreadとGPU間のみでのやり取りとなります。Threadを待つ必要がなく、高速に処理することができます。ブラウザはこのようにスレッドを分けることで画面表示を最適化しています。CSSファイルから画面を生成します。これらの工程を見てみると変更するプロパティーによって関わってくる工程が異なることがわかります。上流の工程に関わる変更は下流の工程にも影響してくるため再計算しなければならない量が多く、パフォーマンスが悪くなる事が見て取れます。ここでは仮に要素の高さや色をjavascriptで変更した場合、どのような処理を行う必要があるのかをおさらいします。大きさや位置の計算はLayoutの処理でしたね。Layoutに関わる変更が発生した場合、上図のようにStyleからすべての工程を再計算する必要があります。このためLayoutに関わる変更は多くの計算を必要とし、パフォーマンスを下げる原因となります。高さを変更した要素が多くの子要素を持っている場合には、子要素の再計算も行う必要があるため注意が必要です。Thread上で動いているので、他のJavascriptの処理に影響されます。box-shadowなどLayout変更時の注意点すべての工程の影響部分を再計算する必要があるため、処理が重い。Threadで計算が必要なので、他の処理を待たせる(または他の処理を待つ)場合がある。Layoutに関わるアニメーションはなるべく行わない。代わりにTransformを使用する。仮に行う必要がある場合は、なるべく子要素を減らし、will-changeの使用を検討する。ある要素の色を変更した場合ある要素の色を変更した場合、Layout(位置や高さ)は関係ないのでスキップされて、Paint、Compositeが呼ばれます。Threadは変わらず使用されるので、処理が重くなることがあります。opacityを代用できる場合にはそちらを使用しましょう。colorなどLayout変更時の注意点Layoutの変更よりは処理が軽い。Threadを使用するのでなるべく使いたくない。opacityで代用できる場合はそちらを優先して使用する。仮に行う必要がある場合は、will-changeの使用を検討する。transformプロパティーでアニメーションした場合transformは一見、Layoutの操作に関係しそうですが、実際にはCompositeの処理の際に呼び出されています。Threadの状態に影響を受けることなく快適に処理することができます。Threadに影響されないため、待ちが発生しづらく快適に動作。計算量が少ないため、快適に動作。opacityを優先的に使用する。注意)実際はレンダリングエンジンによってフローはそれぞれ異なり、Safariが使用しているWebkitではTransformプロパティーの変更でもLayoutから再計算が行われます。Trigersというサイトではプロパティーごとにどの処理が行われているのかを確認することができます。実際には各レンダリングエンジンで処理が異なるため、ここで紹介したフローが絶対ということではありませんが、基本的な仕組みを知っていると、レンダリングに優しい実装が行え、不要なトラブルを避けることができます。画面描写で何かトラブルが合った際にも、見るべきところが何となくわかるので、解決の糸口になるかと思います。Blogにはブログ作成に必要な機能が全て含まれています。Emailのサポートが発表されました。Emailを利用するとWebサイトのような動的な画面をEmail画面上で表示する事ができるようになります。Emailの実装についてご紹介していきます。Emailでは基本的にHTML、CSSを用いてメールを作成することができます。Emailを利用することでする事ができるようになります。Emailを利用するにあたってどの様なことを準備する必要があるかをご説明します。Emailを利用するにはGoogleに利用申請を行い、審査を受ける必要があります。審査は以下の項目を基準に行われるため、事前に準備しておく必要があります。DKIM/SPFとはメールの送信元が偽装されていないことを証明するための認証方式です。AMP版Emailではこれらの認証方式でドメインの認証を行う必要があります。Emailとはのことを指します。Pages)とはGoogleが中心になって開発された技術で、スマートフォンでWebサイトを高速に表示するために現在も利用されています。EmailはこのAMPの技術をEmailでも使用できるように拡張されたもので、すでにウェブサイト用に用いられている文法で動的な画面を簡単に作成することができます。EmailはGmailでのみ受け取り可能ですが、その他のメールプロバイダやメールクライアントも順次対応を表明しています。2019cssパフォーマンス低下の原因をChromeのDevtoolで調査しようこの記事ではChromeのDevtool(開発ツール)を使ったパフォーマンスの調査方法についてご説明致します。アニメーションがカクカクする、画面がちらつくなどの問題が起こった場合にパフォーマンスの観点から問題を解決しましょう。Renderingパネルを用いた調査方法からご説明します。その名の通り、レンダリングに関連するパフォーマンスを調査するためのパネルです。ページ全体でアニメーションを多用していたり、スクロール時に画面下部の表示が遅れる場合などはこのパネルで調査します。