Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ブラウザの制約条件から考えるフロントエンドのリソース設計/Frontend Performan...
Search
forrep
September 26, 2019
Programming
910
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ブラウザの制約条件から考えるフロントエンドのリソース設計/Frontend Performance How to
forrep
September 26, 2019
More Decks by forrep
See All by forrep
サービスに組み込むAIのパターン/アンチパターン
forrep
0
140
Linux && Docker 研修/Linux && Docker training
forrep
28
5.8k
RAGにベクトルDBは必要ない!DBも不要で運用めちゃ楽な RAG Chatbot を作った話
forrep
38
18k
Google Analytics でサイト速度を計測する / Measure site speed with Google Analytics
forrep
2
450
最近コードレビューで指摘したこと
forrep
4
640
「プログラマーのためのCPU入門」は入り口として丁度よい!
forrep
54
38k
DevTools でパフォーマンスチューニング入門 / Introduction to Performance Tuning with DevTools
forrep
2
560
技術的負債に対する視力を得る / How to View Technical Debt
forrep
0
960
しくじり先生 - NFS+sqliteで苦労した話から学ぶ、問題解決の考え方 / problem-solving approach
forrep
1
1.4k
Other Decks in Programming
See All in Programming
レビュー履歴をAIに食わせて、 Compose移行を加速するs
shihochan
0
230
属人化した知識を、 AIが辿れる地図にする
pkshadeck
PRO
1
220
Loosening the Reins: Go Generics Get More Flexible
kuro_kurorrr
0
350
Dockerfile CMD for Node.js
grazie1999
0
140
Detecting Compromised CI with eBPF and Cilium Tetragon
lizrice
0
270
Go 1.27 における memory allocation の高速化
andpad
0
340
自動化したのに回らない テスト運用の壁―AI時代の品質責任と生産性
mfunaki
0
530
go-spidermonkeyでAIエージェントのCode Modeを実装する
syumai
3
1.3k
【QA Test Talk Vol.8】AI-DLC による Whole Team Approach の加速
pkshadeck
PRO
0
250
PyConJP2026_wat_Python × Signal Processing: How to Draw Pictures with Sound Using Spectrogram Art
wat
0
570
複数の Claude Code が"放置"されてしまう問題をCLI ダッシュボードを自作して解決した話
sumihiro3
1
740
Webエンジニアなのにブラウザの仕組みがわからないので、Pythonで自作してみた
tatsuki12
4
1.1k
Featured
See All Featured
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
310
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
480
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
310
How to Talk to Developers About Accessibility
jct
2
530
The Cost Of JavaScript in 2023
addyosmani
55
10k
The Invisible Side of Design
smashingmag
301
52k
Designing for Performance
lara
611
70k
Thoughts on Productivity
jonyablonski
76
5.3k
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.8k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Joys of Absence: A Defence of Solitary Play
codingconduct
1
450
Skip the Path - Find Your Career Trail
mkilby
1
200
Transcript
ブラウザの制約条件から考える フロントエンドのリソース設計 ~ブラウザができること、できないこと~ 1 株式会社ラクーンホールディングス 羽山 純
自己紹介 2
• 名前 ◦ 羽山 純 • 所属 ◦ 株式会社ラクーンホールディングス 技術戦略部
• 技術領域 ◦ サーバーサイド開発 ◦ パフォーマンス改善 ◦ AI開発 (企業審査のAI) • 趣味 ◦ 旅行(ダイビング) ◦ アプリ開発 ? 自己紹介 3
ラクーンホールディングスとは? 事業者間取引(BtoB)の領域で問屋のECサイトや 決済サービス・保証事業を展開 Ruby bizグランプリ2017 「Fintech賞」を受賞 Ruby biz 2017 4
講演について 5
講演について 本日の講演は 以下の寄稿記事をベースとしています。 Software Design 2019年10月号 "速い"Webアプリケーションの作り方 フロントエンド編 「第2章 パフォーマンスを落とさないリソース設計」
本日の講演を面白いと感じていただけた方は 是非、本誌も手に取っていただけたらと思います。 6
デモ環境について 7
デモ環境について 本日はデモ環境で実際に動かしながら進めます。 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
デモ環境について 実際に試してみたい方 ※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
では、始めましょう 10
DOM(Document Object Model)構築とは • HTMLをブラウザがパースしてDOMを構築する • 構築されたDOMは、そのまま画面に表示される • このHTMLファイルを10秒で徐々に転送してみる <!DOCTYPE
html> <head> <meta charset="utf-8"> <title>巨大なHTMLドキュメント</title> <style> body { font-size: 5pt; } </style> </head> <body> <span>▪</span> <span>▪</span> <span>▪</span> <span>▪</span> ・ ・ <span>▪</span> </body> 10,000行 11
DOM(Document Object Model)構築とは デモを見る ※ 2. 巨大なHTMLを10秒で徐々に転送 12
DOM(Document Object Model)構築とは DOM構築 = 画面表示 DOM構築されたら、 即時に画面表示されます。 (実は半分ウソ、真実は後述) 13
JavaScriptとDOM構築のブロック • <script>タグで外部JavaScriptを読み込む。 • <script>読み込みでサーバー側の遅延処理が入る、 転送に5秒間かかる。 <!DOCTYPE html> <head> <meta
charset="utf-8"> <title>03 JavaScriptはDOM構築をブロック</title> </head> <body> <div>この文章はすぐに表示される。</div> <script src="time_5s.js"></script> <div>この文章はJavaScriptが実行された後(5秒後)に表示される</div> </body> 14 time_5s.js ⇒ 5秒遅延
JavaScriptとDOM構築のブロック • time.js は以下の内容で、実行した時間を <body>に「Exec: ◦◦sec」と追記するだけ。 (function() { var tag
= '<div style="color: red;">Exec: ' + (performance.now()/1000).toFixed(2) + 'sec</div>'; var insertTag = function() { document.body.insertAdjacentHTML('beforeend', tag); }; if (document.body) { insertTag(); } else { document.addEventListener("DOMContentLoaded", insertTag); } })(); 15 <body>のDOMを構築済みならそのまま追記 <body>のDOMが未構築なら DOMContentLoadedのタイミングで追記
JavaScriptとDOM構築のブロック デモを見る ※ 3. JavaScriptとDOM構築のブロック 16
JavaScriptとDOM構築のブロック HTML転送 ≠ DOM構築 = 画面表示 <script>はDOM構築をブロックする 「HTML転送済み」≠「DOM構築済み」 17
<script>タグの動的生成(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> <title>Script-Injectによる非同期化</title> </head> <body> <div>Script-InjectはDOM構築をブロックしない、この文章はすぐに表示される。</div> <div>「5秒後」にJavaScriptが実行される</div> </body> 18 5秒遅延
<script>タグの動的生成(Script-Inject) デモを見る ※ 4. Script-Injectによる<script>タグ生成 19
<script>タグの動的生成(Script-Inject) Script-InjectはDOM構築をブロックしない 素晴らしいように思えます が、大きなデメリットが2つあります 20
CSS/CSSOMとレンダリングブロック • <head>で読み込んだCSSは CSSOM(CSS Object Model)が構築されるまで レンダリング(=画面表示)をブロックする • bold.cssは太字・アンダーライン付きにするだけ <!DOCTYPE
html> <head> <meta charset="utf-8"> <link href="bold_5s.css" rel="stylesheet"> <title>CSSOM構築によるレンダリングブロック</title> </head> <body> <div>この文章はCSSOM構築を待つため「5秒後」に表示される。</div> </body> 21 5秒遅延 body { font-weight: bold; text-decoration: underline; }
CSS/CSSOMとレンダリングブロック デモを見る ※ 5. CSSOM構築によるレンダリングのブロック 22
CSS/CSSOMとレンダリングブロック CSSOMの構築完了まで レンダリングはブロックされる ※<head>内で読み込んだ場合のみ CSSOMの構築は DOM構築をブロックしない (DOMContentLoadedはすぐに発火する) 23
画面表示に必要な2つの鍵 画面表示には2つの鍵が必要 DOM構築(都度表示可能) <head>内のCSSOMを完全に構築済み 24
<link>タグの動的生成(CSS-Inject) • CSS-Injectならレンダリングをブロックしない = 非同期化 <!DOCTYPE html> <head> <meta charset="utf-8">
<script> (function() { var tag = document.createElement('link'); tag.href = 'bold_5s.css'; tag.rel = 'stylesheet'; document.head.appendChild(tag); })(); </script> <title>CSS-Injectによるブロック解除</title> </head> <body> <div>この文章はすぐに表示される。</div> <div>「5秒後に」強調表示に変化する。</div> </body> 25 5秒遅延
<link>タグの動的生成(CSS-Inject) デモを見る ※ 8. CSS-InjectによるCSS読み込み 26
<link>タグの動的生成(CSS-Inject) CSS-Injectなら レンダリングをブロックしない Flash of Unstyled Content(FOUC)に注意 ※CSS未適用の画面が表示される現象 27
メディアクエリでレンダリングのブロックを解除 • <link media=print>でレンダリングブロックを解除 = 非同期化 <!DOCTYPE html> <head> <meta
charset="utf-8"> <link href="bold_5s.css" rel="stylesheet" media="print" onload="this.media='all'"> <title>CSSのメディアクエリによるブロック解除</title> </head> <body> <div>この文章はすぐに表示される。</div> <div>「5秒後」に強調表示に変化する。</div> </body> 28 5秒遅延 onloadでallに戻す
メディアクエリでレンダリングのブロックを解除 デモを見る ※ 9. メディアクエリをprintとしてCSS読み込み 29
メディアクエリでレンダリングのブロックを解除 <link media=print>なら レンダリングをブロックしない しかし、転送優先度が最低(Lowest)になってしまう 30 Lowest!!
CSSとJavaScriptの読み込み • CSSとJavaScriptを読み込み • CSSOMの構築はDOM構築をブロックしない <!DOCTYPE html> <head> <meta charset="utf-8">
<link href="bold_3s.css" rel="stylesheet"> <script src="time_5s.js"></script> <title>JavaScriptとCSS読み込み</title> </head> <body> <div>この文章は「5秒後」に表示される。</div> <div>JavaScriptも同じタイミング(5秒後)で実行される。</div> </body> 31 3秒遅延 5秒遅延
CSSとJavaScriptの読み込み デモを見る ※10. CSSとJavaScript読み込み 32
CSSとJavaScriptの読み込み CSSOM構築完了は3秒後。 しかし <script>で5秒間DOM構築が停止、 画面表示は5秒後まで待たされます。 33
CSS読み込みとScript-Inject • JavaScriptをScript-Injectで非同期化 <!DOCTYPE html> <head> <meta charset="utf-8"> <link href="bold_3s.css"
rel="stylesheet"> <script> (function() { var tag = document.createElement("script"); tag.src = 'time_5s.js'; document.head.appendChild(tag); })(); </script> <title>CSS読み込みとScript-Inject</title> </head> <body> <div>この文章は「3秒後」に表示される。</div> <div>JavaScriptは「8秒後」に実行される。</div> </body> 34 3秒遅延 5秒遅延
CSS読み込みとScript-Inject デモを見る ※ 11. CSS読み込みとScript-Inject 35
Script-Injectによる非同期化で 画面表示は3秒後に高速化 しかし Script-Injectの実行がCSSOM構築を待つので 3秒 + 5秒 = 8 秒後にJavaScriptが実行される
これがScript-Injectの問題点の1つです。 CSS読み込みとScript-Inject 36
CSS読み込みとScript-Inject JavaScriptの実行はCSSOMの構築を待つ (外部ファイル・インライン共に) 37
リソースのプリロード機能 ≠ rel=preload • JavaScriptを、通常 ⇒ Script-Injectの順に読み込み <!DOCTYPE html> <head>
<meta charset="utf-8"> <script src="time_5s.js?no=1"></script> <script> (function() { var tag = document.createElement("script"); tag.src = 'time_5s.js?no=2'; document.head.appendChild(tag); })(); </script> <title>通常⇒ScriptInjectで読み込み</title> </head> <body> <div>この文章は「5秒後」に表示されます。</div> <div>JavaScript(1つ目)は「5秒後」に実行されます。</div> <div>JavaScript(2つ目)は「10秒後」に実行されます。</div> </body> 38 5秒遅延 5秒遅延
リソースのプリロード機能 ≠ rel=preload デモを見る ※ 12. JavaScriptを通常読込⇒Script-Inject 39
リソースのプリロード機能 ≠ rel=preload • JavaScriptを、通常読み込み ⇒ 通常読み込み <!DOCTYPE html> <head>
<meta charset="utf-8"> <script src="time_5s.js?no=1"></script> <script src="time_5s.js?no=2"></script> <title>通常⇒ScriptInjectで読み込み</title> </head> <body> <div>この文章は「5秒後」に表示されます。</div> <div>JavaScript(1つ目)は「5秒後」に実行されます。</div> <div>JavaScript(2つ目)も「5秒後」に実行されます。</div> </body> 40 5秒遅延 5秒遅延
リソースのプリロード機能 ≠ rel=preload デモを見る ※ 13. JavaScriptを通常読込⇒通常読込 41
<script src=...>はDOM構築が停止中でも ブラウザのプリロード機能が先読みする。 Script-Injectはプリロード機能が効かない これが2つめの問題点です。 リソースのプリロード機能 ≠ rel=preload 42
• 元々は2008年頃にIE8で実装されたもの • ブラウザによって名称が異なる ◦ Lookahead Downloader (IE8) ◦ Pre-loader
◦ Preload Scanner (WebKit) • 同じ時期にIEは同時Download数を2個⇒6個へ ◦ 現在は同時DL数6個がデファクトとなった • <link rel=preload href=...>と使い分けが必須 ◦ 基本はプリロード機能に任せる ◦ 動的に読み込まれるものには rel=preload リソースのプリロード機能 ≠ rel=preload 43
<script>をasync属性で非同期化 • <script>にasync属性を付けると非同期化する <!DOCTYPE html> <head> <meta charset="utf-8"> <link href="bold_3s.css"
rel="stylesheet"> <script src="time_5s.js" async></script> <title>JavaScriptをasync指定</title> </head> <body> <div>この文章はCSSOM構築を待って「3秒後」に表示されます。</div> <div>JavaScriptは「5秒後(表示されてから2秒後)」に実行されます。</div> </body> 44 3秒遅延 5秒遅延 async指定
<script>をasync属性で非同期化 デモを見る ※ 14. JavaScriptをasync指定 45
<script async>はDOM構築をブロックしない Script-Injectによる非同期化の上位互換 さらにDOM構築停止時のプリロード対象になる ついでにCSSOM構築も待たない 良いことずくめ しかし インライン<script>は非同期化できない問題 <script>をasync属性で非同期化 46
インラインの<script>を非同期化 • 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> <title>インライン<script>によるDOM構築ブロック</title> </head> <body> <div>この文章は「3秒後」に表示される。</div> <div>「8秒後(表示から5秒後)」に文字色が赤に変化する。</div> </body> 47 3秒遅延 5秒後にフォントを赤に変更
インラインの<script>を非同期化 デモを見る ※ 16. インライン<script>によるDOM構築ブロック 48
インラインのJavaScriptでもCSSOM構築を待つ 1. CSS読み込み ⇒ CSSOM構築待ち 2. インライン<script> この順で登場するとCSSOM構築が完了するまで JavaScript実行とDOM構築がブロックされる インラインの<script>を非同期化
49
インラインの<script>を非同期化 • 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> <title>インライン<script>の非同期化</title> </head> <body> <div>この文章は「3秒後」に表示される。</div> <div>「5秒後(表示から2秒後)」の文字色が赤に変化する。</div> </body> 50 3秒遅延 インラインJavaScriptを データURLに変換 ※実際には空白なしで1行
インラインの<script>を非同期化 デモを見る ※ 17. インライン<script>の非同期化 51
インライン<script>も遅延原因になる 実行にはCSSOMの完全な構築が必要 可能な限り非同期化しましょう もしくは CSS読み込み前に<script>を移動するだけでもOK インラインの<script>を非同期化 52
まとめ 53
まとめ • HTML転送 ≠ DOM構築 = 画面表示 • <script>はDOM構築をブロックする •
CSSOM構築完了までレンダリングをブロックする ※ただし<head>内で読み込んだCSSのみ • 画面表示は2つの鍵が必要 ◦ DOM構築(部分的な構築でもOK) ◦ <head>内のCSSOMを完全に構築済 • JavaScript実行はCSSOM構築を待つ • インラインJavaScriptでもCSSOM構築を待つ 54
最後に 55
56 https://www.raccoon.ne.jp/company/recruit/index.html
ご清聴ありがとうございました。 https://www.raccoon.ne.jp/company/recruit/index.html