wwwave'sTechblog |ウェイブのエンジニアブログ

株式会社ウェイブのエンジニアによるテックブログです。会社の話や Ruby、Vue.jsについてなど技術的な話をしていきたいと思います。

ブラウザレンダリングの仕組み

ウェイブでエンジニアをしているKSです!

本記事では、Chrome DevTools を用いてレンダリングプロセスをご説明致します!

参照サイトとして、私が所属してるプロジェクトCoolmic(スマホ版のみ)を使用致します。
※ 2020/02/15 時点でのレンダリングの流れとなります。Coolmic では、現在、パフォーマンス改善を行っており、レンダリングの流れが変わる可能性がありますのでご了承ください。

f:id:sys_wwwave:20200304112008p:plain

web エンジニアの方は、自社サイトなどと照らし合わせながら本記事を読んでいただけると、イメージ湧きやすいかと思います!!

はじめに

Coolmic では、サイトパフォーマンス改善がミッションとなりました。 しかし、レンダリングの仕組みを断片的にしか把握していなかったため、適切な改善が行えない状況でした。 そこで、パフォーマンス改善前にレンダリングの仕組みを勉強し、社内勉強会を開催したため、その発表内容の一部を本記事にまとめました!

DevTools は回線を Fast 3G にしております!

注意事項

DevTools の使い方に関しては、本記事では説明致しません。
メルカリの記事が大変参考になりますので、ご参照ください。

また、パフォーマンス指標の FCP のタイミングまでのレンダリングプロセスについてまとめております。
パフォーマンス指標に関しても説明致しませんので、ご了承下さい。

レンダリングの仕組み

全体把握

◆ プロセス

大きく4つのプロセスがあります。
ただ、Painting の後に Scripting のプロセスが実行されることもあります。
以下の流れは一例と思っていただいて問題ありません。

f:id:sys_wwwave:20200303182838p:plain

◆ 全体イメージ図

f:id:sys_wwwave:20200303200745p:plain

1. Download HTML(Loading)

レンダリングに必要な HTML のデータをダウンロード。
※ 時系列をわかりやすくするため、あえて HTML のダウンロードのみ項目を分けました。

f:id:sys_wwwave:20200303114118p:plain

↓ 拡大

f:id:sys_wwwave:20200303114410p:plain

2. Loading

2-1. Parse

レンダリングエンジンが処理しやすいデータ構造体にデータ変換。

  • HTMLファイル → DOM Tree
  • CSSファイル → Style Rules(CSSOMツリー)

◆ DOM Tree

f:id:sys_wwwave:20200303194438p:plain

◆ Style Rules

f:id:sys_wwwave:20200303194456p:plain

◆ Devtools

f:id:sys_wwwave:20200303114845p:plain

2-2. Download Resources

リソース(Javascript, CSS, Image)のダウンロード。

◆ Devtools

f:id:sys_wwwave:20200303114852p:plain

3. Scripting

Javascript が実行されるタイミング( = レンダリングブロック )。
Evaluate Script のタイミングで Javascript が実行されている。

◆ Devtools

f:id:sys_wwwave:20200303114845p:plain

↓拡大

f:id:sys_wwwave:20200303124805p:plain

★レンダリングブロックについて

ここで、一度レンダリングプロセスから話を脱線致します。
先ほどの Loading フェーズで、Parse 完了までの時間が長いと感じた方も多いかと思います。
私自身もその一人です。

Q なぜ、FCP までの時間の大半を占めているのか🤔
A レンダリングブロックされているから😇

f:id:sys_wwwave:20200303154552p:plain

では、具体的に、何がレンダリングブロックに繋がっているというと、各リソース(HTML, CSS, Javascript)すべてが原因なり得ます。

◆ HTML, CSS の場合

レンダリング ツリーを構築するために DOM と CSSOM の両方が必須です。
そのため、HTML と CSS の両方がレンダリング ブロック リソース と言えます。

ファーストビューで不必要なデータを含めず、できるだけ早くレスポンスを返すことができるようにすることが重要です。
PageSpeed Insights でも重要なcssはインラインで配信することをおすすめされております。

◆ Javascript の場合

script タグを適切に使用しないと、大幅なレンダリングブロック発生原因となります。
重要なのは、属性の種類です。

サイト仕様に合わせて、async or defer どちらかを適切に使用することが重要です。

各属性のJavascriptダウンロード、実行タイミングが時系列でわかるこちらの記事も合わせてご参照いただけるとわかりやすいです。

属性 Javascript実行タイミング
なし(デフォルト) Parse をストップさせ、ダウンロード完了後、即座に実行される。
async 非同期でダウンロード完了後、即座に実行される
defer 非同期でダウンロードし、Parse 完了を待つ
Parse 後かつ DOMContentLoaded 前に実行される

4. Rendering

4-1. CalculateStyle

DOM Tree と Style Rules を紐づけて、Render Tree 生成。
具体的には、どのスタイルがどの要素に適用されるのかをマッチングさせる。

◆ Render Tree

f:id:sys_wwwave:20200303194639p:plain

◆ Devtools

f:id:sys_wwwave:20200303120201p:plain

↓拡大

f:id:sys_wwwave:20200303120223p:plain

4-2. Layout

Render Tree を元に、要素の位置と大きさの情報を考慮した Layout Tree 生成。
以下の影響するプロパティーの変更があった場合に呼ばれる。

height, width, padding, margin, top, right, left, bottom, box-shadowなど

◆ Layout Tree

f:id:sys_wwwave:20200303194818p:plain

◆ Devtools

f:id:sys_wwwave:20200303120201p:plain

↓拡大

f:id:sys_wwwave:20200303120324p:plain

5. Painting

5-1. Paint

画面上への描写指示、Paint Records の作成。
並列で、Layer Tree も作成。

◆ Paint Records

Layout Tree を元に、要素の重なり(z-index)を考慮した Paint Records を生成。

f:id:sys_wwwave:20200303194841p:plain

◆ Layer Tree

Layout Treeを元に、Layer Tree(レイヤーツリー)を作成します。
Layer の分離は要素の変更の際の計算量を少なくする。

f:id:sys_wwwave:20200303194922p:plain

◆ Devtools

f:id:sys_wwwave:20200303120201p:plain

↓拡大

f:id:sys_wwwave:20200303120420p:plain

5-2. CompositeLayers

Main Thread に代わって Compositor Thread が実行。
まず、先程の作成した Paint Records を元に各レイヤーのピクセル単位で色を当て込んでいきます。
ラスタライズが完了したレイヤーは一つの合成レイヤーにまとめられ最終的にGPUに渡され画面に表示されます。

Painting の処理完了後、サイトが描画されます!

◆ Devtools

f:id:sys_wwwave:20200303120201p:plain

↓拡大

f:id:sys_wwwave:20200303120448p:plain

おわりに

レンダリングプロセスのどこに時間がかかっているか把握することができました🤗
最良なパフォーマンス改善を行うためには、仕組みの理解が大切だと改めて感じました。

また、パフォーマンス改善内容はサイトによって様々だと思いますので、目標設定に応じた適切な対応が必要です。

次回予告

Coolmic パフォーマンス改善してみた 〜 FP 1秒への道 〜

参考サイト

GitLab PagesでStorybookを公開するまでの道!!!

ウェイブに新卒入社してから3年目に突入したKSです!

今回は、私の関わっているプロジェクトで、GitLab Pagesを用いてStorybook確認用のWebサイト(社内用)を公開したのでご紹介いたします。

本記事内容は、GitLab Pagesの導入がメインとなります。

そのため、GitLabやStorybookの導入に関しては、触れておりません。
その他、公開するリポジトリの状態として、StorybookはWebpack管理されており、Docker化されております。
各種設定に関してもあくまで一例参考にしていただけたらと思います。

GitLab Pagesとは

GitLab Pagesは、GitLabのリポジトリから静的なWebサイトを直接公開できる機能です。
独自ドメインを設定してWebサイトを公開することも可能です。
すでにGitLab環境があれば、簡単に導入することができます。

GitLab Pages導入背景

常に最新のStorybookを確認する方法として静的Webサイトを公開する必要がありました。
そこで、今回採用したのが、GitLab Pagesです。

GitLab Pagesの導入には以下のメリットがありました。

  • 弊社でGitLabを使用しているため、別途サーバーを立ち上げる必要がない
  • Storybookの更新自動化(CI/CD)
  • アクセス制限が容易に行える(後述)

といいつつも、、、

実際導入となると、ハマりポイントがありました😭
次に、ハマりポイントにも触れつつ、GitLab PagesでStorybookを公開までの流れについてご説明致します。

GitLab PagesでStorybookを公開するまでの流れ

事前準備

今回、GitLab Pagesを導入した方法は、公式ドキュメントに記載されている中のWildcard domains with TLS supportで対応しました。
この方法では、事前準備として以下の対応が必要でした。

  1. ワイルドカードDNSの登録によるドメイン取得
  2. ワイルドカードSSL証明書の取得

SSL証明書は、Let's Encryptで取得いたしました。
今回は、1プロジェクトでお試し導入のため、ドメインを指定してSSL証明書を取得し使用いたしました。

GitLab Pages有効化

GitLab PagesはデフォルトでOFFになっております。
GitLabの設定ファイルを修正し、GitLab PagesをONにする必要があります。 まずは、gitlab.rbに以下のコードを追加致します。

## gitlab.rb

gitlab_pages['enable'] = true
pages_nginx['redirect_http_to_https'] = true

# 事前準備で取得したドメイン
pages_external_url 'https://hogehoge.jp'

# 事前準備で取得したSSL証明書
pages_nginx['ssl_certificate'] = "/fullchain.pem"
pages_nginx['ssl_certificate_key'] = "/privkey.pem"

設定を追加したら、以下のコマンドで設定を反映させます。

$ gitlab-ctl reconfigure

GitLab Pagesにアクセス制限追加

今回導入するStorybookは社内向けのため、外部からのアクセス制限を追加する必要がありました。
アクセス制限を追加する方法として、以下の2通りの方法がありました。

  1. Access controlを用いたアクセス制限方法(GitLab Pagesの追加機能)
  2. Nginxを用いたアクセス制限方法

今回は、2.の方法でアクセス制限を追加致しました。
ここで、Access controlでアクセス制限しないのか?と疑問に思う方もいるかと思います。
ここだけの話、実は、最初にAccess controlでアクセス制限を追加していたのですが、不具合が発生し導入を断念致しました😇
発生した不具合に関しては後述致します。

次に、それぞれの導入方法についてご説明致します。

1. Access controlを用いたアクセス制限方法

Access controlとは、GitLab 11.5で追加された新機能です。
アクセス制限の種類は公式ドキュメントに明記されております。
この機能を用いて、リポジトリごとにアクセス制限を追加することが可能です。

Access controlを追加するための設定はgitlab.rbに以下のコードを追加し再起動するだけです。

## gitlab.rb

gitlab_pages['access_control'] = true

再起動後、リポジトリの設定でSettings > General > Permissions内にPagesという項目が追加されます。
ここで、アクセス制限の種類を選択することが可能です。

ここまでの流れだと、特に導入時にハマりポイントはありませんでしたが、、、

次に、なぜ導入を断念したかご説明致します。

Access control不具合内容

Access control導入後、数日間は問題なく動作しておりました。
しかし、しばらくすると、Pagesにアクセスした際、503エラーが発生しアクセス拒否になってしまいました。

なぜ?🤔

原因に関して調査しましたが、解決の糸口を見つけることはできませんでした。
似たような挙動になる旨のIssuesはありましたが、原因を特定したIssuesは存在せず、かなりハマってしまったため、今回は導入を断念する結果となりました。

ただし、要件は外部からのアクセス制限を追加することだったため、スピード感を重視し、別の方法での実装に切り替えました。

2. Nginxを用いたアクセス制限方法

アクセス制限とありますが、実際にはIPアドレス制限です。
具体的な対応方法としては、GitLab Pagesで自動生成されるNginxの設定ファイルに、別途IPアドレス制限用のファイルを読み込ませます。 こちらも対応方法はシンプルです。

まずは、外部読み込み用のconfファイルを作成致します。

## hogehoge.conf

# 許可するIPアドレス
allow ×××.×××.×××.×××;
deny all;

次に、gitlab.rbに以下のコードを追加し、先ほど作成したconfファイルを読み込むように設定致します。

## gitlab.rb

pages_nginx['custom_gitlab_server_config'] = "include /etc/gitlab/hogehoge.conf;"

修正は以上で、最後に再起動すればアクセス制限完了です。

GitLab PagesでStorybook公開

単純にhtmlファイルを公開するのみであれば、公式ドキュメントの設定で公開することができます。
今回は、build-storybook.shというStorybook公開用のスクリプトを作成して実行しております。
追加、修正したファイルは以下の通りです。(追加分以外は省略しております。)
重要なポイントにコメントしております。

## .gitlab-ci.yml

stages:
  - pages

# この名称はpages固定にする必要があります。
pages:
  stage: pages
  script:
    - ./scripts/build-storybook.sh
  artifacts:
    paths:
    - public
  only:
    - master
## package.json

{
  "scripts": {
    "build-storybook": "build-storybook -c .storybook -o .public -s ./.storybook/"
  },
}
## build-storybook.sh

set -o errexit

mkdir .public
mkdir public/storybook

IMAGE_NAME=${RANDOM}
CONTAINER_NAME=${RANDOM}
docker build ./frontend -t ${IMAGE_NAME}

# コンテナ内でstorybookをbuildし、公開用のファイル生成
docker run --name="${CONTAINER_NAME}" ${IMAGE_NAME} npm run build-storybook

# コンテナ内で生成されたファイルをコンテナからホストへコピー
docker cp ${CONTAINER_NAME}:/app/.public/. ./.public/
docker rm $CONTAINER_NAME
docker rmi $IMAGE_NAME

# 最終的に公開するファイルをpublic配下にしておけばOK
mv .public/* public/storybook

注意事項として、公式ドキュメントにも記述がある通り、公開できるディレクトリはpublic配下となります。
そのため、別のディレクトリを公開対象に設定するとアクセスできないためご注意ください。

最後に、修正した内容をリポジトリにpushし、masterブランチにマージすると、Storybookが公開されているかと思います!!
今回の設定だと、https://[Group名].hogehoge.jp/[Repository名]/storybook/index.html でアクセスすることができます!

f:id:sys_wwwave:20190930145914p:plain

おわりに

最後まで読んでいただきありがとうございました!
本記事の内容が少しでも参考になればと思います。

今回ご紹介したGitLab Pagesの導入は、弊社で初めてとなりました。
ウェイブでは、このような新しい技術に挑戦しやすい環境が整っております。
今後も、新しい技術にどんどん挑戦してスキルアップしていきたいです🤗

トップに戻る