Slide 1

Slide 1 text

EPDVNFOUXSJUF࠶ߟ

Slide 2

Slide 2 text

Who Name 青野健利 GitHub @brn Twitter @brn227 What Dev Lead at 株式会社AI Shift Contributor of V8 Javascript Engine

Slide 3

Slide 3 text

8IBUJTEPDVNFOUXSJUF

Slide 4

Slide 4 text

document.writeとは What is document.write 文字列で渡されたhtml断片をhtml中に挿入する

Slide 5

Slide 5 text

document.writeとは What is document.write 壊れたhtml断片でもよしなに対応してくれる

Slide 6

Slide 6 text

document.writeとは What is document.write document.write("
") document.write('foo') document.write('
') 分割も可能

Slide 7

Slide 7 text

1SPTPGEPDVNFOUXSJUF

Slide 8

Slide 8 text

document.writeのメリット Pros of document.write ブラウザのレンダリングプロセスへ介入できる

Slide 9

Slide 9 text

Insertion Point Pros of document.write ブラウザは現在レンダリングしているポイントを指す Insertion pointを持っている この値はレンダリング中のdocument位置を 相対位置で保持している

Slide 10

Slide 10 text

Redering Interception Pros of document.write このレンダリングに介入する方法は document.write以外には存在しない

Slide 11

Slide 11 text

How to use redering interception Pros of document.write Renderingに介入できるメリット 画面が表示されたときには必ず処理が終わっている保証がある事 使い所 - 計測タグ等 - ページレンダリングまでに必ず必要な処理

Slide 12

Slide 12 text

$POTPGEPDVNFOUXSJUF

Slide 13

Slide 13 text

document.writeのデメリット Pros of document.write ブラウザのレンダリングプロセスへ介入してしまう

Slide 14

Slide 14 text

document.writeのデメリット Cons of document.write document.writeが記述されている外部scriptは常に同期的に ロードされる必要があり、外部script含め処理完了までレンダリ ングが完全にブロックされてしまう

Slide 15

Slide 15 text

document.writeのデメリット Cons of document.write document.writeは実行タイミングがずれるとページを壊す

Slide 16

Slide 16 text

document.writeのspec Cons of document.write whatwgに一応dynamic markup insertion という形で項目があります

Slide 17

Slide 17 text

document.writeのspec Cons of document.write 1.If document is an XML document, then throw an "InvalidStateError" DOMException. 2.If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException. 3.If document's active parser was aborted is true, then return. 4.If the insertion point is unde fi ned, then: 1.If document's unload counter is greater than 0 or document's ignore-destructive-writes counter is greater than 0, then return. 2.Run the document open steps with document. 5.Insert input into the input stream just before the insertion point. 6.If document's pending parsing-blocking script is null, then have the HTML parser process input, one code point at a time, processing resulting tokens as they are emitted, and stopping when the tokenizer reaches the insertion point or when the processing of the tokenizer is aborted by the tree construction stage (this can happen if a script end tag token is emitted by the tokenizer).

Slide 18

Slide 18 text

document.writeのspec Cons of document.write If the insertion point is unde fi ned, then: 1.If document's unload counter is greater than 0 or document's ignore- destructive-writes counter is greater than 0, then return. 2.Run the document open steps with document.

Slide 19

Slide 19 text

EPDVNFOUPQFO

Slide 20

Slide 20 text

Spec of document.open document.open 1. If document is an XML document, then throw an "InvalidStateError" DOMException exception. 2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException. 3. Let entryDocument be the entry global object's associated Document. 4. If document's origin is not same origin to entryDocument's origin, then throw a "SecurityError" DOMException. 5. If document has an active parser whose script nesting level is greater than 0, then return document.
 This basically causes document.open() to be ignored when it's called in an inline script found during parsing, while still letting it have an e ff ect when called from a non-parser task such as a timer callback or event handler. 6. Similarly, if document's unload counter is greater than 0, then return document.
 This basically causes document.open() to be ignored when it's called from a beforeunload, pagehide, or unload event handler while the Document is being unloaded. 7. If document's active parser was aborted is true, then return document.
 This notably causes document.open() to be ignored if it is called after a navigation has started, but only during the initial parse. See issue #4723 for more background. 8. If document's node navigable is non-null and document's node navigable's ongoing navigation is a navigation ID, then stop loading document's node navigable. 9. For each shadow-including inclusive descendant node of document, erase all event listeners and handlers given node. 10. If document is the associated Document of document's relevant global object, then erase all event listeners and handlers given document's relevant global object. 11. Let oldFlag be the value of document's fi re mutation events fl ag. 12. Set document's fi re mutation events fl ag to false. 13. Replace all with null within document. 14. Set document's fi re mutation events fl ag to oldFlag. 15. If document is fully active, then: 1. Let newURL be a copy of entryDocument's URL. 2. If entryDocument is not document, then set newURL's fragment to null. 3. Run the URL and history update steps with document and newURL. 16. Set document's is initial about:blank to false. 17. If document's iframe load in progress fl ag is set, then set document's mute iframe load fl ag. 18. Set document to no-quirks mode. 19. Create a new HTML parser and associate it with document. This is a script-created parser (meaning that it can be closed by the document.open() and document.close() methods, and that the tokenizer will wait for an explicit call to document.close() before emitting an end-of- fi le token). The encoding con fi dence is irrelevant. 20. Set the insertion point to point at just before the end of the input stream (which at this point will be empty). 21. Update the current document readiness of document to "loading".
 This causes a readystatechange event to fi re, but the event is actually unobservable to author code, because of the previous step which erased all event listeners and handlers that could observe it. 22. Return document.

Slide 21

Slide 21 text

Spec of document.open Pros of document.write 意訳: 全部消します

Slide 22

Slide 22 text

͋ΕɺԿ͕໰୊ͳΜ͚ͩͬʁ

Slide 23

Slide 23 text

API design あれ何が問題なんだっけ? document.writeは適切に使われる分にはそこまで問題ではない ただし、適切に使うのが非常に難しいAPI Interfaceになっている

Slide 24

Slide 24 text

API design あれ何が問題なんだっけ? 文字列でDOMを渡す都合上 そこに何が紛れていても実行されてしまう 簡単に使えるが何も考えず使うと大惨事になる

Slide 25

Slide 25 text

API design あれ何が問題なんだっけ? DOMが主流になる前のscriptはdocument.writeを多用してい たこともあり、品質の悪いdocument.writeが多い

Slide 26

Slide 26 text

݁࿦ "1*͕ѱ͍

Slide 27

Slide 27 text

$IBMMFOHF

Slide 28

Slide 28 text

document.writeへの介入 document.write alternate Chrome 55以降でdocument.writeへブラウザが介入する

Slide 29

Slide 29 text

document.writeへの介入条件 document.write alternate • ユーザーの接続速度が遅い場合 • document.write() はトップレベルのドキュメントにある場合 • スクリプトが同じoriginでホストされている • スクリプトがブラウザの HTTP キャッシュにまだ存在しない • 再読み込みではない場合

Slide 30

Slide 30 text

document.writeへの介入しない条件 document.write alternate • ユーザーの接続速度に問題がない • iframe 内の document.write スクリプトの場合 • async 属性または defer 属性が指定されたscript • eTLD+1 が一致するスクリプト(www.example.org に挿入された js.example.org でホストされているスクリプトなど) • スクリプトがブラウザの HTTP キャッシュに存在する • 再読み込みの場合

Slide 31

Slide 31 text

document.writeを非同期化してみる document.write alternate document.write自体は上書き可能なので 中身を非同期化することである程度はパフォーマンスはよくなる

Slide 32

Slide 32 text

document.writeを非同期化してみる document.write alternate ただしinsertion pointのコントロールができない 同期実行を前提としたscriptが壊れる 複数行のdocument.writeが機能しない といった問題は解消しない

Slide 33

Slide 33 text

ແཧ

Slide 34

Slide 34 text

document.writeを非同期化してみる document.write alternate https://github.com/eligrey/async-document-write https://github.com/iamnoah/writeCapture

Slide 35

Slide 35 text

3FOEFSCMPDLJOH

Slide 36

Slide 36 text

blocking属性 Render Blocking blocking属性・rel=blockingを策定中 Blinkには実装済み 特定の要素にたどり着くまで画面描画を停止する https://github.com/WICG/view-transitions/blob/ main/document-render-blocking.md

Slide 37

Slide 37 text

何がうれしいか? Render Blocking 画面のチラツキを防ぐ(例. メニューが次々増えたり、トップ画像が 次々出てきたり)目的 document.writeでやろうとしていたことが実現できる

Slide 38

Slide 38 text

blocking属性 Render Blocking id the-foldの要素にたどり着くまで、描画を停止する

Slide 39

Slide 39 text

blocking属性 Render Blocking html全体のレンダリングをblockする …

Slide 40

Slide 40 text

blocking属性 Render Blocking 特定の位置までblockするには..

Slide 41

Slide 41 text

// The set of element IDs that should block rendering. let blockingElementIds = new Set(); function maybeUnblockRendering() { if (blockingElementIds.size == 0) { document.documentElement.blocking=""; } } // The value returned by getState() is set by the old Document. // For example, using IntersectionObserver. // // It tracks whether the old Document added a `view-transition-name` // to the header based on its visibility. if (navigation.initialLoad.from().getState().morphHeader) { blockingElementIds.add("header-id"); } maybeUnblockRendering(); …
...
// When an element is parsed, remove it from the blocking set and // unblock rendering if all blocking elements have been parsed. blockingElementIds.delete("header-id"); maybeUnblockRendering();

Slide 42

Slide 42 text

͓ΘΓ