レンダリング¶
はじめに¶
本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。
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処理とは¶
記入中...