how amp work?
TRANSCRIPT
HOW�AMP�WORKS?�TaiHoon�Kim(NAVER)�[email protected]
about�me
TaiHoon�Kim�[email protected]�NAVER�Front-End�Platform�
SADI�HTML5�invited�professorNHN�NEXT�WebUI�Basic�adjunct�professor�Project�Management�Professional(PMP)�
Front-End�Development�Group�Sysop�http://facebook.com/groups/webfrontend
AMP?�
Bench-marking�
How�AMP�works?�
AMP�Inside
�AGENDA
AMP?�
ACCELLERATED�MOBILE�PAGES�by�Google�
ウェブページをもっと早くレンダリングするための技術 パフォーマンス向上のためのグーグルの経験に基づいたベストプラクティス
AMPs�are�just�Web�Pages!
AMP�HTML�
拡張可能な�HTML�:�AMP�拡張タグとプロパーティ�
AMP�拡張タグを使って<amp-carousel>�などのコンポーネント利
⽤AMP�拡張タグはカスタムエレメント基盤のウェブコンポーネント�
パフォーマンスのための制約�:�<img>�は�<amp-img>�を使うなど
amp.js�
AMP�ページとリソースのライフサイクル及びレンダリングタイム制御�
AMP�カスタムエレメント実装コード�
ウェブページのパフォーマンス向上のためのベストプラクティス�
AMP�は外部の�JS�を許可してない。�カスタム�JS�は�sandbox�iframe�で使える。
AMP�STYLE�=�CSS
外部のスタイル、インラインスタイルは許可しない�ドキュメントに注⼊する形のインラインスタイルは使⽤可能�最⼤�50KB�まで許可�
例�
Naver�モバイルメインページ�-�合計�2つのファイルで約�100KB�Naver�モバイルニュースホーム�-�合計�2つのファイルで約�67KB�
Naver�モバイルニュースエンド�-�合計�8つのファイルで約�50KB
<style amp-custom> /* any custom style goes here */ body { background-color: white; } amp-img { background-color: gray; border: 1px solid black; }</style>
AMP�コンポーネントを使って�HTML�を作成し�
AMP�JS�をロードすることで�
ウェブページの⾼速レンダリングを保証�
�+�グーグル検索結果で即時ロード��+�グーグル�CDN�キャッシュ�
HELLO�AMP!!!
<!doctype html><html amp lang="en"> <head> <meta charset="utf-8"> <title>Hello, AMPs</title> <link rel="canonical" href="http://example.ampproject.org/article-metadata.html" /> <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <style amp-boilerplate>body{ ... }</style> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body> <h1>Welcome to the mobile web</h1> </body></html>
AMP�COMPONENTS
/* AMP 基本コンポ〡ネント - イメ〡ジ */<amp-img src="welcome.jpg" alt="Welcome" height="400" width="800"></amp-img>
/*AMP 基本コンポ〡ネント - 動画 */
<amp-video width=400 height=300 src="https://yourhost.com/videos/myvideo.mp4" poster="myvideo-poster.jpg"></amp-video>
amp-ad amp-embed amp-imgamp-pixel amp-video
AMP�EXTENDED�COMPONENT
... 35 more
AMP-CAROUSEL
/* AMP 拡張コンポーネント - カル〡セル */
<amp-carousel width=300 height=400> <amp-img src="my-img1.png" width=300 height=400></amp-img> <amp-img src="my-img2.png" width=300 height=400></amp-img> <amp-img src="my-img3.png" width=300 height=400></amp-img></amp-carousel>
<script async custom-element="amp-carousel" src="https://cdn.ampproject.org/v0/amp-carousel-0.1.js"></script>
AMP�EXTENDED�COMPONENT/* AMP コンポ〡ネント - lightbox */<amp-image-lightbox id="lightbox1" layout="nodisplay"></amp-image-lightbox>
AMP�EXTENDED�COMPONENT/* AMP コンポ〡ネント - サイドバ〡 */<amp-sidebar id='sidebar' layout="nodisplay" side="right"> ...
AMP�ベンチマーク�
NAVAR�TV芸能�AMP�プロトタイピング�
AMP�ベンチマークイシュー�動画�動作する。ただし�URL�は�https���placeholder�⽤のイメージが必要�
JS�カスタム�JS�は使えない。コメントと�TOP�ボタンが動作しない�sandbox�iframe�で実装しようともしたが、セキュリティ問題など発⽣�nClick�は�amp-analytics�で対応できるかリサーチが必要�
外部�CSS�ロードを許可しない�HTML�ヘッダーにスタイル注⼊�4KB�HTML�エレメントにインラインスタイルは許可しない
AMP�ベンチマーク�TIMELINE�
localhost�でnetwork�throttling�4Mb↓,�3Mb↑,�20ms�RTT�に設定
AMP�ベンチマーク�NETWORK�
AMP�未適応
AMP�ベンチマーク�NETWORK�
AMP�適応�
画⾯に表⽰されるリソースを先にダウンロード�スクロースの外側のリソースは順次に遅延ロード�
HOW�AMP�SPEEDS�UP�PERFORMANCE
How�AMP�Speeds�Up�Performance�
⾮同期スクリプトのみ許可�ページのレンダリングを遮断する�JS�防⽌�AMP�JS�以外の他の�JS�も許可しない�
全てのリソースのサイズ指定�リソースをダウンロードせずにサイズと位置を計算してレイアウトしておく�
拡張コンポーネントをレンダリングを遮断せずに実⾏可能�追加で�HTTP�リクエストはあるかもしれないが、ページのレイアウトとレンダリングは遮断しない
How�AMP�Speeds�Up�Performance�
3rd�Party�JS�をクリティカルパースから除外�3rd�Party�JS�は�sandbox�iframe�の内部だけ許可�親ページの実⾏を遮断しなくて性能に影響しない�
CSS�は�HTML�内部スタイルシートのみ許可しサイズを制限�HTML�ページの内部に�style�タグを⼊れて使⽤�スタイルシートのサイズは�50KB�までに制限�
Web�フォントを効率的にダウンロード�ブラウザを遮断する�HTTP�リクエストが発⽣いないように�Web�フォントをダウンロード
How�AMP�Speeds�Up�Performance�
スタイル再計算の最⼩化�不必要なレイアウト防⽌のため、先に測定をして変更を後に処理�フレーム毎スタイルので⼀度だけ再計算できるように保証�
GPU�加速アニメーションのみ実⾏�ページのレイアウトが発⽣しない�CSS�アニメーションのみ使⽤�transform,�opacity
How�AMP�Speeds�Up�Performance�
リソーツのロード順番を制御�ダウンロードするリソースの優先順位を計算�重要なリソースからダウンロード�
ページの即時ロード�preconnect�API�取得するページをビューポート分だけインポート
INSIDE�OF�AMP
AMP:�CUSTOM�ELEMENTS
export function registerElement(win, name, implementationClass) { knownElements[name] = implementationClass;
win.document.registerElement(name, { prototype: createAmpElementProto(win, name), }); }
src/custom-element.js
export function installImg(win) { registerElement(win, 'amp-img', AmpImg); }
builtins/amp-img.js
AMP:�CUSTOM�ELEMENTS
Custom Element
BaseElement HTMLElement.prototype
AmpImg ElementProto
this.implementation_ = new AmpImg();
AMP�Components�=�HTML�Custom�Elements
新しい�HTML�タイプを定義しライフサイクルを制御
createdCallback�カスタムエレメントの登録する時に発⽣
attachedCallback�カスタムエレメントを�DOM�に追加する時に発⽣
detachedCallback�カスタムエレメントを�DOM�から削除する時に発⽣
attributeChangedCallback�カスタムエレメントの属性を追加、修正、削除する時に発⽣
AMP:�CUSTOM�ELEMENT
Lifecycle�Callbacks
firstAttachedCallback�カスタムエレメントの最初の�DOM�に追加する時に発⽣
buildCallback�カスタムエレメントと⼦エレメントが使⽤可能になった時に発⽣
layoutCallback�AMP�がカスタムエレメントをレンダリングする時に発⽣
viewportCallback�カスタムエレメントがビューポートに⼊る時と出る時に発⽣
documentInactiveCallback�ドキュメントをアンロードする前に発⽣
AMP:�CUSTOM�ELEMENT
AMP's�Extended�Lifecycle�Callbacks
AMP:�FSM(Finite-State�Machine)
AMPはコンポーネントの条件によって⼀つの状態をもつ�条件が変わると別の状態に変わる�状態が変わるとそれに伴うアクションを実⾏する
OFF ON
turn on
turn off
AMP:�FSM(Finite-State�Machine)
prerender
paused
inactive
doPass
unload
resume
doPass
doPass
doPass
doPass
doPassdoPass
doPassdoPass
doPass
paused
paused
unload
unload
doPass
doPassvisible
hidden
AMP:�FSM(Finite-State�Machine)doPass�=�discoverWork�+�work
applySizesAndMediaQuery
discoverWork
measure
scheduleLayoutOrPreload
setInViewport
calcTaskScore
peek
startLayout
work
AMP:�FSM(Finite-State�Machine)
applySizesAndMediaQueryメディアクエリを適応して�AMP�サイズ関連の属性をパース
measureリソースのサイズと位置を測定。リソースは�READY_FOR_LAYOUT�状態になる
scheduleLayoutOrPreloadビューポートを拡張した特定領域内のリソースをレイアウトするための�Job�スケジューリング�
setInViewportリソースがビューポートの内にあるのか外にあるのかを計算し�viewportCallback�を実⾏��
discoverWork
AMP:�FSM(Finite-State�Machine)
calcTaskScoreスケジューリングした�Job�の優先順位を計算�
1)�どの�AMP�カスタムエレメントなのか?�2)�どんな作業なのか?�3)�ビューポートはどれくらい近いのか?�
peek優先順位が最も⾼い�Job�を選択
startLayout選択した�Job�の対象リソースを�LAYOUT_SCHEDULED�状態に変更しレイアウトを開始�例えば、リソースが�amp-img�だとすると�src�属性にイメージを指定することでレイアウト可能�
work
AMP:�SINGLE�PASS�PROCESS
class Pass { constructor(win, handler, opt_defaultDelay) { this.handler_; this.defaultDelay_; this.scheduled_; this.nextTime; this.running_; } isPending(); schedule(opt_delay); cancel(); }
//�実⾏する�Job�を�Pass�に登録�new Pass(this.win, () => this.doPass_());
//�Job�Queue�の最後の実⾏時間を元に次の実⾏時間を計算�let nextPassDelay = (now - this.exec_.getLastDequeueTime()) * 2; nextPassDelay = Math.max(Math.min(30000, nextPassDelay), 5000);
AMP:�PROMISE
initialize src
call layoutComplete
AMP:�OPTIMIZATION�REFLOWReflow�
Width�と�Height�などエレメントの幾何学的構造に影響する属性を変更すると�該当エレメントのサイズを位置をまた計算�
エレメントの変更が影響する他のエレメントのサイズと位置もまた計算�
Reflow�は�Repaint�を発⽣させるので�できるだけ�Reflow�と�Repaint�を少なくする⽅がいい
AMP:�OPTIMIZATION�REFLOW
枠の厚さを変更し⾼さを変更する場合�
厚さ変更�→�Reflow�→�Repaint→�⾼さ変更→�Reflow�→�Repaint�
厚さ変更�Job�を保存�→�⾼さ変更�Job�保存�→�保存された�Job�実⾏�→�Reflow�→�Repaint�
ブラウザは変更作業を集めて必要な時に実⾏
AMP:�OPTIMIZATION�REFLOWエレメントのサイズ、位置、スクロールに関連した�スタイル属性をリクエストすると?�
ブラウザはリクエストしたスタイルの属性を値を正確に計算するために�保存されていた変更作業を実⾏し�Reflow、Repaint�する�
スタイル属性をリクエストする時には不必要な�Reflow�が発⽣しないように
AMP:�OPTIMIZATION�REFLOW
// measure - (保存された Job があると) Reflow 発生
var h1 = element1.clientHeight;
// mutate - 変更 Job 保存
element1.style.height = (h1 * 2) + 'px';
// measure - Reflow 発生
var h2 = element2.clientHeight;
// mutate - 変更 Job 保存
element2.style.height = (h2 * 2) + 'px';
// measure - Reflow 発生
var h3 = element3.clientHeight;
// mutate - 変更 Job 保存
element3.style.height = (h3 * 2) + 'px';
AMP:�OPTIMIZATION�REFLOW
// measure
var h1 = element1.clientHeight; // (保存された Job があると) 発生
var h2 = element2.clientHeight; // 保存された Job なし
var h3 = element3.clientHeight; // 保存された Job なし
// mutate
element1.style.height = (h1 * 2) + 'px'; // 変更Job 保存
element2.style.height = (h2 * 2) + 'px'; // 変更Job 保存
element3.style.height = (h3 * 2) + 'px'; // 変更Job 保存
NO�batching�DOM�read/write�operations
batching�DOM�read/write�operations
AMP:�OPTIMIZATION�REFLOW
class Vsync { .. .. mutate(mutator) { ... } measure(measurer) { ... } run(task, opt_state) { ... } .. .. }
AMP:�OPTIMIZATION�REFLOW
for (let i = 0; i < tasks.length; i++) { if (tasks[i].measure) { tasks[i].measure(states[i]); } }
for (let i = 0; i < tasks.length; i++) { if (tasks[i].mutate) { tasks[i].mutate(states[i]); } }
AMP:�OPTIMIZATION�REFLOW
THANK�YOU