Slide 1

Slide 1 text

ブラウザの制約条件から考える フロントエンドのリソース設計 ~ブラウザができること、できないこと~ 1 株式会社ラクーンホールディングス 羽山 純

Slide 2

Slide 2 text

自己紹介 2

Slide 3

Slide 3 text

● 名前 ○ 羽山 純 ● 所属 ○ 株式会社ラクーンホールディングス 技術戦略部 ● 技術領域 ○ サーバーサイド開発 ○ パフォーマンス改善 ○ AI開発 (企業審査のAI) ● 趣味 ○ 旅行(ダイビング) ○ アプリ開発 ? 自己紹介 3

Slide 4

Slide 4 text

ラクーンホールディングスとは? 事業者間取引(BtoB)の領域で問屋のECサイトや 決済サービス・保証事業を展開 Ruby bizグランプリ2017 「Fintech賞」を受賞 Ruby biz 2017 4

Slide 5

Slide 5 text

講演について 5

Slide 6

Slide 6 text

講演について 本日の講演は 以下の寄稿記事をベースとしています。 Software Design 2019年10月号 "速い"Webアプリケーションの作り方 フロントエンド編 「第2章 パフォーマンスを落とさないリソース設計」 本日の講演を面白いと感じていただけた方は 是非、本誌も手に取っていただけたらと思います。 6

Slide 7

Slide 7 text

デモ環境について 7

Slide 8

Slide 8 text

デモ環境について 本日はデモ環境で実際に動かしながら進めます。 Node.js製の簡易ウェブサーバを利用します。 https://github.com/forrep/web-performance 覚えておくルールは1つだけ。 ファイル名パターンで転送遅延させてます。 hoge.html ⇒ 遅延なし hoge_10s.html ⇒10秒遅延してから転送(TTFB =10秒) hoge_10s_g.html ⇒10秒間で徐々に転送(TTFB = 0秒) 8

Slide 9

Slide 9 text

デモ環境について 実際に試してみたい方 ※Node.js v4以降が必要、追加ライブラリ不要 本日のイベントで利用する環境には以下のURLで アクセスできます。 http://localhost:8080/event/ 9 $ git clone https://github.com/forrep/web-performance.git $ cd web-performance $ node server.js Starting on http://localhost:8080

Slide 10

Slide 10 text

では、始めましょう 10

Slide 11

Slide 11 text

DOM(Document Object Model)構築とは ● HTMLをブラウザがパースしてDOMを構築する ● 構築されたDOMは、そのまま画面に表示される ● このHTMLファイルを10秒で徐々に転送してみる 巨大なHTMLドキュメント body { font-size: 5pt; } ・ ・ 10,000行 11

Slide 12

Slide 12 text

DOM(Document Object Model)構築とは デモを見る ※ 2. 巨大なHTMLを10秒で徐々に転送 12

Slide 13

Slide 13 text

DOM(Document Object Model)構築とは DOM構築 = 画面表示 DOM構築されたら、 即時に画面表示されます。 (実は半分ウソ、真実は後述) 13

Slide 14

Slide 14 text

JavaScriptとDOM構築のブロック ● タグで外部JavaScriptを読み込む。 ● <script>読み込みでサーバー側の遅延処理が入る、 転送に5秒間かかる。 <!DOCTYPE html> <head> <meta charset="utf-8"> <title>03 JavaScriptはDOM構築をブロック</title> </head> <body> <div>この文章はすぐに表示される。</div> <script src="time_5s.js">
この文章はJavaScriptが実行された後(5秒後)に表示される

Slide 15

Slide 15 text

JavaScriptとDOM構築のブロック ● time.js は以下の内容で、実行した時間を に「Exec: ○○sec」と追記するだけ。 (function() { var tag = '
Exec: ' + (performance.now()/1000).toFixed(2) + 'sec
'; var insertTag = function() { document.body.insertAdjacentHTML('beforeend', tag); }; if (document.body) { insertTag(); } else { document.addEventListener("DOMContentLoaded", insertTag); } })(); 15 のDOMを構築済みならそのまま追記 のDOMが未構築なら DOMContentLoadedのタイミングで追記

Slide 16

Slide 16 text

JavaScriptとDOM構築のブロック デモを見る ※ 3. JavaScriptとDOM構築のブロック 16

Slide 17

Slide 17 text

JavaScriptとDOM構築のブロック HTML転送 ≠ DOM構築 = 画面表示 はDOM構築をブロックする 「HTML転送済み」≠「DOM構築済み」 17

Slide 18

Slide 18 text

タグの動的生成(Script-Inject) ● Script-InjectはDOM構築をブロックしない = 非同期化 <!DOCTYPE html> <head> <meta charset="utf-8"> <script> (function() { var tag = document.createElement('script'); tag.src = 'time_5s.js'; document.head.appendChild(tag); })(); Script-Injectによる非同期化
Script-InjectはDOM構築をブロックしない、この文章はすぐに表示される。
「5秒後」にJavaScriptが実行される
18 5秒遅延

Slide 19

Slide 19 text

タグの動的生成(Script-Inject) デモを見る ※ 4. Script-Injectによる<script>タグ生成 19

Slide 20

Slide 20 text

タグの動的生成(Script-Inject) Script-InjectはDOM構築をブロックしない 素晴らしいように思えます が、大きなデメリットが2つあります 20

Slide 21

Slide 21 text

CSS/CSSOMとレンダリングブロック ● で読み込んだCSSは CSSOM(CSS Object Model)が構築されるまで レンダリング(=画面表示)をブロックする ● bold.cssは太字・アンダーライン付きにするだけ CSSOM構築によるレンダリングブロック
この文章はCSSOM構築を待つため「5秒後」に表示される。
21 5秒遅延 body { font-weight: bold; text-decoration: underline; }

Slide 22

Slide 22 text

CSS/CSSOMとレンダリングブロック デモを見る ※ 5. CSSOM構築によるレンダリングのブロック 22

Slide 23

Slide 23 text

CSS/CSSOMとレンダリングブロック CSSOMの構築完了まで レンダリングはブロックされる ※内で読み込んだ場合のみ CSSOMの構築は DOM構築をブロックしない (DOMContentLoadedはすぐに発火する) 23

Slide 24

Slide 24 text

画面表示に必要な2つの鍵 画面表示には2つの鍵が必要 DOM構築(都度表示可能) 内のCSSOMを完全に構築済み 24

Slide 25

Slide 25 text

タグの動的生成(CSS-Inject) ● CSS-Injectならレンダリングをブロックしない = 非同期化 (function() { var tag = document.createElement('link'); tag.href = 'bold_5s.css'; tag.rel = 'stylesheet'; document.head.appendChild(tag); })(); CSS-Injectによるブロック解除
この文章はすぐに表示される。
「5秒後に」強調表示に変化する。
25 5秒遅延

Slide 26

Slide 26 text

タグの動的生成(CSS-Inject) デモを見る ※ 8. CSS-InjectによるCSS読み込み 26

Slide 27

Slide 27 text

タグの動的生成(CSS-Inject) CSS-Injectなら レンダリングをブロックしない Flash of Unstyled Content(FOUC)に注意 ※CSS未適用の画面が表示される現象 27

Slide 28

Slide 28 text

メディアクエリでレンダリングのブロックを解除 ● でレンダリングブロックを解除 = 非同期化 CSSのメディアクエリによるブロック解除
この文章はすぐに表示される。
「5秒後」に強調表示に変化する。
28 5秒遅延 onloadでallに戻す

Slide 29

Slide 29 text

メディアクエリでレンダリングのブロックを解除 デモを見る ※ 9. メディアクエリをprintとしてCSS読み込み 29

Slide 30

Slide 30 text

メディアクエリでレンダリングのブロックを解除 なら レンダリングをブロックしない しかし、転送優先度が最低(Lowest)になってしまう 30 Lowest!!

Slide 31

Slide 31 text

CSSとJavaScriptの読み込み ● CSSとJavaScriptを読み込み ● CSSOMの構築はDOM構築をブロックしない JavaScriptとCSS読み込み
この文章は「5秒後」に表示される。
JavaScriptも同じタイミング(5秒後)で実行される。
31 3秒遅延 5秒遅延

Slide 32

Slide 32 text

CSSとJavaScriptの読み込み デモを見る ※10. CSSとJavaScript読み込み 32

Slide 33

Slide 33 text

CSSとJavaScriptの読み込み CSSOM構築完了は3秒後。 しかし で5秒間DOM構築が停止、 画面表示は5秒後まで待たされます。 33

Slide 34

Slide 34 text

CSS読み込みとScript-Inject ● JavaScriptをScript-Injectで非同期化 (function() { var tag = document.createElement("script"); tag.src = 'time_5s.js'; document.head.appendChild(tag); })(); CSS読み込みとScript-Inject
この文章は「3秒後」に表示される。
JavaScriptは「8秒後」に実行される。
34 3秒遅延 5秒遅延

Slide 35

Slide 35 text

CSS読み込みとScript-Inject デモを見る ※ 11. CSS読み込みとScript-Inject 35

Slide 36

Slide 36 text

Script-Injectによる非同期化で 画面表示は3秒後に高速化 しかし Script-Injectの実行がCSSOM構築を待つので 3秒 + 5秒 = 8 秒後にJavaScriptが実行される これがScript-Injectの問題点の1つです。 CSS読み込みとScript-Inject 36

Slide 37

Slide 37 text

CSS読み込みとScript-Inject JavaScriptの実行はCSSOMの構築を待つ (外部ファイル・インライン共に) 37

Slide 38

Slide 38 text

リソースのプリロード機能 ≠ rel=preload ● JavaScriptを、通常 ⇒ Script-Injectの順に読み込み (function() { var tag = document.createElement("script"); tag.src = 'time_5s.js?no=2'; document.head.appendChild(tag); })(); 通常⇒ScriptInjectで読み込み
この文章は「5秒後」に表示されます。
JavaScript(1つ目)は「5秒後」に実行されます。
JavaScript(2つ目)は「10秒後」に実行されます。
38 5秒遅延 5秒遅延

Slide 39

Slide 39 text

リソースのプリロード機能 ≠ rel=preload デモを見る ※ 12. JavaScriptを通常読込⇒Script-Inject 39

Slide 40

Slide 40 text

リソースのプリロード機能 ≠ rel=preload ● JavaScriptを、通常読み込み ⇒ 通常読み込み 通常⇒ScriptInjectで読み込み
この文章は「5秒後」に表示されます。
JavaScript(1つ目)は「5秒後」に実行されます。
JavaScript(2つ目)も「5秒後」に実行されます。
40 5秒遅延 5秒遅延

Slide 41

Slide 41 text

リソースのプリロード機能 ≠ rel=preload デモを見る ※ 13. JavaScriptを通常読込⇒通常読込 41

Slide 42

Slide 42 text

はDOM構築が停止中でも ブラウザのプリロード機能が先読みする。 Script-Injectはプリロード機能が効かない これが2つめの問題点です。 リソースのプリロード機能 ≠ rel=preload 42

Slide 43

Slide 43 text

● 元々は2008年頃にIE8で実装されたもの ● ブラウザによって名称が異なる ○ Lookahead Downloader (IE8) ○ Pre-loader ○ Preload Scanner (WebKit) ● 同じ時期にIEは同時Download数を2個⇒6個へ ○ 現在は同時DL数6個がデファクトとなった ● と使い分けが必須 ○ 基本はプリロード機能に任せる ○ 動的に読み込まれるものには rel=preload リソースのプリロード機能 ≠ rel=preload 43

Slide 44

Slide 44 text

をasync属性で非同期化 ● <script>にasync属性を付けると非同期化する <!DOCTYPE html> <head> <meta charset="utf-8"> <link href="bold_3s.css" rel="stylesheet"> <script src="time_5s.js" async> JavaScriptをasync指定
この文章はCSSOM構築を待って「3秒後」に表示されます。
JavaScriptは「5秒後(表示されてから2秒後)」に実行されます。
44 3秒遅延 5秒遅延 async指定

Slide 45

Slide 45 text

をasync属性で非同期化 デモを見る ※ 14. JavaScriptをasync指定 45

Slide 46

Slide 46 text

はDOM構築をブロックしない Script-Injectによる非同期化の上位互換 さらにDOM構築停止時のプリロード対象になる ついでにCSSOM構築も待たない 良いことずくめ しかし インライン<script>は非同期化できない問題 <script>をasync属性で非同期化 46

Slide 47

Slide 47 text

インラインのを非同期化 ● async属性はインライン<script>に適用できない <!DOCTYPE html> <head> <meta charset="utf-8"> <link href="bold_3s.css" rel="stylesheet"> <script> (function() { setTimeout( function(){document.body.style.color = 'red'}, 5000 ) })(); インライン<script>によるDOM構築ブロック
この文章は「3秒後」に表示される。
「8秒後(表示から5秒後)」に文字色が赤に変化する。
47 3秒遅延 5秒後にフォントを赤に変更

Slide 48

Slide 48 text

インラインのを非同期化 デモを見る ※ 16. インライン<script>によるDOM構築ブロック 48

Slide 49

Slide 49 text

インラインのJavaScriptでもCSSOM構築を待つ 1. CSS読み込み ⇒ CSSOM構築待ち 2. インライン この順で登場するとCSSOM構築が完了するまで JavaScript実行とDOM構築がブロックされる インラインの<script>を非同期化 49

Slide 50

Slide 50 text

インラインのを非同期化 ● srcにデータURLで指定することでasyncを有効化 ● data:text/javascript, に続けてJavaScriptコードを エスケープした文字列を貼り付け ● エスケープはJavaScriptならencodeURI() <!DOCTYPE html> <head> <meta charset="utf-8"> <link href="bold_3s.css" rel="stylesheet"> <script async src="data:text/javascript,(function()%7BsetTimeout(function()%7Bdo⏎ cument.body.style.color='red'%7D,5000)%7D)()"> インライン<script>の非同期化
この文章は「3秒後」に表示される。
「5秒後(表示から2秒後)」の文字色が赤に変化する。
50 3秒遅延 インラインJavaScriptを データURLに変換 ※実際には空白なしで1行

Slide 51

Slide 51 text

インラインのを非同期化 デモを見る ※ 17. インライン<script>の非同期化 51

Slide 52

Slide 52 text

インラインも遅延原因になる 実行にはCSSOMの完全な構築が必要 可能な限り非同期化しましょう もしくは CSS読み込み前に<script>を移動するだけでもOK インラインの<script>を非同期化 52

Slide 53

Slide 53 text

まとめ 53

Slide 54

Slide 54 text

まとめ ● HTML転送 ≠ DOM構築 = 画面表示 ● はDOM構築をブロックする ● CSSOM構築完了までレンダリングをブロックする ※ただし<head>内で読み込んだCSSのみ ● 画面表示は2つの鍵が必要 ○ DOM構築(部分的な構築でもOK) ○ <head>内のCSSOMを完全に構築済 ● JavaScript実行はCSSOM構築を待つ ● インラインJavaScriptでもCSSOM構築を待つ 54

Slide 55

Slide 55 text

最後に 55

Slide 56

Slide 56 text

56 https://www.raccoon.ne.jp/company/recruit/index.html

Slide 57

Slide 57 text

ご清聴ありがとうございました。 https://www.raccoon.ne.jp/company/recruit/index.html