レンダリング¶
はじめに¶
本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。
01. レンダリングの仕組み¶
構成する処理¶
以下の8つの処理からなる。
クライアントの操作のたびにイベントが発火し、Scriptingプロセスが繰り返し実行される。
- Downloading
- Parse
- Scripting
- Rendering
- CalculateStyle
- Paint
- Rasterize
- Composite

01-02. マークアップ言語¶
▼ マークアップ言語とは¶
ハードウェアが読み込むファイルには、バイナリファイルとテキストファイルがある。
このうち、テキストファイルをタグとデータによって構造的に表現し、ハードウェアが読み込める状態する言語のこと。
▼ マークアップ言語の歴史¶
Webページをテキストによって構成するための言語をマークアップ言語という。
1970年、IBMが、タグによって、テキスト文章に構造や意味を持たせるGML言語を発表した。

xml 形式:Extensible Markup Language¶
▼ xml 形式とは¶
テキストファイルのうち、何らかのデータの構造を表すことに特化している。
▼ スキーマ言語とは¶
マークアップ言語の特に xml 形式で、タグの付け方は自由である。
しかし、利用者間で共通のルールを設けたほうが良い。
ルールを定義するための言語をスキーマ言語という。
スキーマ言語に、DTD:Document Type Definition (文書型定義) がある。
*実装例*
<!DOCTYPE Employee[
<!ELEMENT Name (First, Last)>
<!ELEMENT First (#PCDATA)>
<!ELEMENT Last (#PCDATA)>
<!ELEMENT Email (#PCDATA)>
<!ELEMENT Organization (Name, Address, Country)>
<!ELEMENT Name (#PCDATA)>
<!ELEMENT Address (#PCDATA)>
<!ELEMENT Country (#PCDATA)>
]>
html 形式:HyperText Markup Language¶
▼ html 形式とは¶
テキストファイルのうち、Webページの構造を表すことに特化している。
01-03. JavaScript¶
マークアップ言語へのJavaScriptの組み込み¶
▼ インラインスクリプト¶
js ファイルを直接的に組み込む。
<script>
document.write("JavaScriptを直接的に組み込んでいます。");
</script>
▼ 外部スクリプト¶
外部 js ファイルを組み込む。
<script src="sample.js"></script>
CDN (グローバルなキャッシュサーバー) の仕組みを使用して、Web上からWebページを取得もできる。
<script
src="https://cdn.jsdelivr.net/npm/lazyload@2.0.0-rc.2/lazyload.min.js"
integrity="sha256-WzuqEKxV9O7ODH5mbq3dUYcrjOknNnFia8zOyPhurXg="
crossorigin="anonymous"
></script>
▼ scriptタグが複数ある場合¶
1 個のWebページの html ファイル内で、scriptタグが複数に分散していても、Scriptingプロセスでは、1つにまとめて実行される。
そのため、より上部のscriptタグの処理は、より下部のscriptに引き継がれる。
1。
例えば、以下のコードがある。
localNum
<p>見出し1</p>
<script>
var globalNum = 10;
</script>
<p>見出し2</p>
<script>
globalNum = globalNum * 10;
</script>
<p>見出し3</p>
<script>
document.write("<p>結果は" + globalNum + "です</p>");
var foo = true;
</script>
<script src="sample.js"></script>
// sample.js
// 無名関数の即時実行。定義と呼び出しを同時に実行する。
(function () {
// 外側の変数 (foo) を参照できる。
if (foo) {
console.log("外部ファイルを読み出しました");
}
var localNum = 20;
function localMethod() {
// 外側の変数 (localNum) を参照できる。
console.log("localNum");
}
// 定義した関数を実行
localMethod();
})();
- 実行時には以下の様に、まとめて実行される。
ここでは、html ファイルで定義した関数の外にある変数は、グローバル変数になっている。
1 個のページを構成する html ファイルを別ファイルとして分割していても、同じである。
<script>
var globalNum = 10;
localNum = localNum * 10;
document.write("<p>結果は" + num + "です</p>");
var foo = true;
// 無名関数の即時実行。定義と呼び出しを同時に実行する。
(function () {
// 外側の変数 (foo) を参照できる。
if (foo) {
console.log("外部ファイルを読み出しました");
}
var localNum = 20;
function localMethod() {
// 外側の変数 (localNum) を参照できる。
console.log("localNum");
}
// 定義した関数を実行
localMethod();
})();
</script>
01-04. ブラウザのバージョン¶
Polyfill¶
▼ Polyfillとは¶
JavaScriptやHTMLの更新にブラウザが追いついていない場合、それを補完するように実装されたパッケージのこと。
『Polyfilla』に由来している。
02. Downloading処理¶
Downloading処理とは¶
▼ 非同期的な読み出し¶
まず、サーバーサイドからWebページ (html ファイル、.css ファイル、js ファイル、画像ファイル) は、分割されながら、バイト形式でレスポンスされる。
これは、メッセージボディに含まれている。
これを優先度を基に読み込む処理。
分割でレスポンスされたWebページを、随時読み込んでいく。
そのため、各Webページの読み出しは非同期的に行われる。
Downloading処理が終了したWebページから、次のParse処理に進んでいく。
▼ Webページの要素の優先順位¶
(1)-
HTML
(2)-
CSS
(3)-
JS
(4)-
画像
Pre-Loading¶
▼ Pre-Loadingとは¶
Downloading処理の優先順位を上げるように宣言する。
優先度の高い分割Webページは、次のParse処理、Scripting処理も行われる。
そのため、JSファイルのScripting処理が、以降のimageファイルのDownloading処理よりも早くに行われることがある。
<head>
<meta charset="utf-8" />
<title>Title</title>
<!-- preloadしたいものを宣言 -->
<link rel="preload" href="style.css" as="style" />
<link rel="preload" href="main.js" as="script" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>Hello World</h1>
<script src="main.js" defer></script>
</body>
Lazy Loading (遅延読み出し)¶
▼ Lazy Loadingとは¶
条件に合致した要素を随時読み込む。
条件の指定方法には、scroll/resize イベントに基づく方法と、Intersection Observerによる要素の交差率に基づく方法がある。
画像ファイルの遅延読み出しでは、読み出し前にダミー画像を表示させておき、遅延読み出し時にダミー画像パスを本来の画像パスに上書きする。
▼ scrollイベントとresizeイベントに基づく遅延読み出し¶
scrollイベントとresizeイベントを監視し、これらのイベントの発火をトリガーにして、画面内に新しく追加された要素を随時読み込む。
▼ Intersection Observerの仕組みに基づく遅延読み出し¶
Intersection Observerの仕組みでは、特定のHTML要素を指定し、これがほかの要素とどのくらい交差しているかを非同期に検知できる。
指定の交差率を超えて、初めてその要素を読み込む。
例えば、交差率の閾値を『0.5』と設定すると、ターゲットエレメントの交差率が『0.5』を超えた要素を随時読み込む。

*実装例*
import {useRef, useState, useEffect, type ReactNode} from "react";
type
LazyRenderWithIntersectionObserverProps = {
/**
* レンダリングを遅延させたい要素
*/
children: ReactNode;
/**
* レンダリング遅延中のプレースホルダーの高さ
*
* レンダリング遅延中は本来あるべき要素がなくなるため、仮の空欄を挿入する
*/
minHeight: number;
/**
* レンダリング開始の検知に使用する領域
*
* スクロールによって ref.current 要素が rootMargin 内に入ると、遅延させていたレンダリングが始まる
*/
rootMargin? : string;
};
/**
* Intersection Observerの仕組みを使用して、渡した要素のレンダリングを遅延させる
* @see https://developer.mozilla.org/ja/docs/Web/API/IntersectionObserver
*/
export const LazyRenderWithIntersectionObserver = ({
children,
minHeight,
rootMargin = "200px",
}: LazyRenderWithIntersectionObserverProps)
:
JSX.Element
=>
{
const ref = useRef < HTMLDivElement > (null);
const [shouldRender, setShouldRender] = useState(false);
useEffect(() => {
// 一度レンダリングを開始した後の場合
if (shouldRender) {
return;
}
// ref.current 要素を取得できない場合
const targetElement = ref.current;
if (!targetElement) {
return;
}
const intersectionObserver = new IntersectionObserver(
([entry]) => {
// ref.current 要素が rootMargin 内に入った場合
if (entry.isIntersecting) {
setShouldRender(true);
intersectionObserver.disconnect();
}
},
{rootMargin}
);
intersectionObserver.observe(targetElement);
return () => intersectionObserver.disconnect();
}, [rootMargin, shouldRender]);
return (
<div ref={ref}>
{shouldRender ? children : <div style={{minHeight}}/>}
</div>
);
}
;
<LazyRenderWithIntersectionObserver
minHeight={120}
children={
// ここにレンダリングを遅延させたいの要素を設定する
}
/>
Eager Loading¶
▼ Eager Loadingとは¶
02-02. Parse処理¶
Parse処理とは¶
Downloading処理によって読み込まれたWebページを翻訳するプロセス
html 形式テキストファイルの構造解析¶
▼ 構造解析の流れ¶
Downloading処理で読みこまれたバイト形式ファイルは、文字コードを基に、一連の文字列に変換される。
ここでは、以下の html ファイルと .css ファイル (style.css) に変換されたとする。
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link href="style.css" rel="stylesheet" />
<title>Critical Path</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg" /></div>
<div style="width: 50%">
<div style="width: 50%">Hello world!</div>
</div>
</body>
</html>
/* style.css */
body {
font-size: 16px;
}
p {
font-weight: bold;
}
span {
color: red;
}
p span {
display: none;
}
img {
float: right;
}
Webページの文字列からHTMLタグが認識され、トークンに変換される。
各トークンは、1 個のオブジェクトに変換される。

HTMLパーサーは、オブジェクトをノードとして、DOMツリーを作成する。
DOMツリーを作成する途中でscriptタグに到達すると、いったん、JSファイルを読み込んでScripting処理を終えてから、DOMツリーの作成を再開する。
DOMのインターフェースについては、以下のリンクを参考にせよ。

同時に、CSSパーサーは、head タグにある link タグを基にサーバーへリクエストを送信する。
レスポンスされた .css ファイルに対してDownloading処理を行った後、オブジェクトをノードとして、CSSOMツリーを作成する。

xml 形式テキストファイルの構造解析¶
▼ 構造解析の流れ¶
レンダリングエンジンは、最初に出現するルート要素を根 (ルート) として扱う。さらに、すべての要素や属性を、そこから延びる枝葉として意味づける。これにより、レンダリングツリーを作成する。
*例*

03. Scripting処理¶
Scripting処理とは¶
サーバーサイドからWebページ (html ファイル、.css ファイル、js ファイル、画像ファイル) を取得した後、レンダリングエンジンのHTMLの解析が script タグに到達する。
レンダリングエンジンは、JavaScriptエンジンに script タグの内容を渡す。
JavaScriptエンジンは、JavaScriptコードを機械語に翻訳し、実行する。
この処理は、初回アクセス時のみでなく、イベントが発火したときにも実行される。
JavaScriptエンジン¶
▼ JavaScriptエンジンとは¶
JavaScriptのインタプリタのこと。
JavaScriptエンジンは、レンダリングエンジンから html ファイルに組み込まれたJavaScriptのコードを受け取る。
JavaScriptエンジンは、これを機械語に翻訳し、ハードウェアに対して命令する。

▼ 機械語翻訳¶
JavaScriptエンジンは、コードを、字句解析、構造解析、意味解釈、命令の実行をコード1行ずつに対し、繰り返し実行する。
03-02. イベント¶
イベント¶
▼ イベントとは¶
ブラウザの各操作はイベントとして .js ファイルまたは html ファイルに紐付けられている。
▼ イベントハンドラ関数とは¶
イベントの発火に伴ってコールされる関数のこと。
イベントハンドラ関数が実行されるたびにScripting処理が繰り返される。
html 形式におけるイベントハンドラ関数のコール¶
▼ onload¶
『Webページのローディング』というイベントが発火すると、イベントハンドラ関数をコールする。
▼ onclick¶
『要素のクリック』というイベントが発火すると、イベントハンドラ関数をコールする。
<input type="button" value="ボタン1" onclick="methodA()" />
<script>
function methodA() {
console.log("イベントが発火しました");
}
</script>
JS形式におけるイベントハンドラ関数のコール¶
▼ document.getElementById.onclick 関数¶
指定したIDに対して、1 個のイベントと 1 個のイベントハンドラ関数を紐付ける。
*実装例*
// 指定したIDで、クリックイベントが発火した時に、処理を実行する。
document.getElementById("btn").onclick = () => {
console.log("イベントが発火しました");
};
▼ document.addEventListener 関数¶
1 個のイベントに対して、1つ以上のイベントハンドラ関数を紐付ける。
第一引数で、click などのイベントを設定し、第二引数で関数 (無名関数でも可) を渡す。
false を設定することにより、イベントバブリングを行わせない。
*実装例*
<button id="btn">表示</button>
<script>
const btn = document.getElementById("btn");
btn.addEventListener(
"click",
() => {
console.log("クリックされました!");
},
false,
);
</script>
// DOMContentLoadedイベントが発火した時に、処理を実行する。
document.addEventListener("DOMContentLoaded", () => {
console.log("イベントが発火しました");
});
// 1つ目
document.getElementById("btn").addEventListener(
"click",
() => {
console.log("イベントが発火しました`(1)`");
},
false,
);
// 2つ目
document.getElementById("btn").addEventListener(
"click",
() => {
console.log("イベントが発火しました`(2)`");
},
false,
);
04. Rendering処理¶
Rendering処理とは¶
レンダリングツリーが作成され、ブラウザ上のどこに何を描画するのかを計算する。
CalculateStyle処理とLayout処理に分けられる。
04-02. CalculateStyle処理¶
CalculateStyle処理とは¶
レンダリングエンジンは、DOMツリーのルートのノードから順にCSSOSツリーを適用し、Renderツリーを作成する。

04-03. Layout処理¶
Layout処理とは¶
上記で読み込まれた html 形式テキストファイルには、ネストされた 2 つの div がある。
1 つ目 (親) の div より、ノードの表示サイズをビューポートの幅の 50% に設定する。
この親に含まれている 2 つ目 (子) の div より、その幅を親の50%、つまりビューポートの幅の25%になるようにレイアウトされる。

05. Paint処理¶
Paint処理とは¶
DOMツリーの各ノードを、ブラウザ上に描画する。
05-02. Rasterize処理¶
Rasterize処理とは¶
記入中...
05-03. CompositeLayers処理¶
CompositeLaysers処理とは¶
記入中...