この記事は Google I/O '19 のセッションの視聴メモです。
想定読者は自分なので正確性や網羅率には問題があるかもしれません。
References
Speaker(s): Shubhie Panicker and Nicole Sullivan
Abstract
Chromeがオープンソースプロジェクトと協力して行ってきたWebプラットフォームとツール、フレームワークの改善について紹介します。
Contents
1. Year in review: collaborating with frameworks
Chunking code
シングルスレッドでユーザーからの入力にただちに応答するためには、メインスレッドで行われている処理を細切れに分割していつでも割り込みができるようにして置かなければならない。発表者のチームはタップ時の遅延について分析を行った。
- ユーザーの操作がブロックされるまでに10msから50msの時間がかかっていた
- React や Vue などのライブラリはレンダープロセスを小さなチャンクに分けている
- よってレンダリングサイクルの途中でもユーザーの入力を受け付けることができる
- これは各ライブラリのすぐれたスケジューラーによって実現されているが、もちろんそのスケジューラーはアプリ全体の面倒を見ることはできない
- フレームワークは次のシグナル情報がないためそれが行えない
- Fetch responses
- Script tag responses
- Callbacks e.g. onreadystatechange in xhr
- Browser internal work like garbage collection
- というわけで全く新しいスケジューラーを作ることにした
Scheduling API
スケジューラーの説明
- JSの処理はプライオリティレベルで4つに分けられる
- 緊急でない処理をレンダリングをブロックしない処理に移動する
- Defaultレベルとなるプライオリティにありとあらゆるタスクが入ってしまっていて整理がつかない
- そこでDefaultレベルのタスクをさらにHigh/Medium/Lowにわけた
- プロトタイプを作り始めたばかり
- https://github.com/WICG/main-thread-scheduling
- 下記のような感じでスケジューラーにタスクを登録していけば良いらしい
function importantWork() {
...
}
let task = scheduler.postTask(importantWork, {priority: 'high'});
isInputPending
- isInputPending というAPIをChromeに追加した
- Reactなどのライブラリがユーザーの入力があるかどうかを判断するのが簡単になった
- Facebookの人がChromiumに追加した
Display Locking
- JSにより操作されるどのDOMがレンダリングされる必要があるのかをブラウザが知れるようになった
- スクロールコンテンツやタブ・ウィジェットなど様々なユースケースでパフォーマンスを発揮する
2. Nuanced Performance Goals
Page load time and SPAs
- SPAサイトの開発者は常に速いけど意味のない画面の表示と遅いけど意味のある画面の表示というトレードオフと戦っている
- ちなみに後者を選びがち
- SPAサイトの遷移時間は測定するのが難しい
- 発表者の目的は開発者がそのようなトレードオフをしなくてよくなること
Budgets for total resource sizes
- critical resources の量は1ページに付き170kbに収めたほうが良い
- コードもデータも全てで170kb
- Code Splitting と SSR が重要になってくる
3. Secrets of facebook & google internal tools
- インクリメンタルローディング
- Google Hotel の事例
- ユーザーの入力を受け付けるまで時間がかかりすぎている
- ほとんどのユーザーが使わない機能の処理は無駄
- 見た目はできていているのでユーザーが入力しようとするが、裏で処理が走っていて入力できない時間がある
- uncanny valley (不気味の谷)と呼ばれていた
- 反応がないと思って何回もクリックするやつ
- ページが複雑だとSSRが非常に重くなってしまう
- → Interaction driven late-loading
- フォームだけ用意して処理はあとからダウンロードする
- 基本的にはプリロードしておくことでネットワークの遅延による影響を最小にする
- jsaction というGoogle製のライブラリを使ってイベントをデリゲートする
- 真のイベントハンドラーがロードされるまでユーザーのクリックイベントをキューイングする
- イベントと真のハンドラーをDispatcherが紐付ける
- コードの次はデータ
- 初期データはフッターに入れてる
- Streaming SSR
- Smart SSRとも呼ばれていた
- 主要なコンポーネントごとにSSRを行い順次表示するSSR(?)
- HTTP Cascade from native code splitting
- コードに Code split point を設け、段階的にレンダリングしていくこと
- Facebookでは未レンダリング要素をLoading要素として表示している
- ReactのLazyとかSuspenseの話かな?
まとめ(下図)
- FacebookとGoogleはこれらの要素に完全に独立に到達した
- それ自体がこのリストの正当性を証明していると言える
4. Bundle bloat and npm modules
- 不必要なJSをどう扱うか
- JSバンドルの中に真に不必要なコードが20%から30%ほど含まれているということはまったく珍しいことではない
- npmモジュールの中のJSをどう扱うかが重要なのだが、GoogleもFacebookもどうすれば良いかわかってない
- この問題がしんどいのは次の3つの理由が主な理由
-
- 過度なトランスパイルがJSサイズの膨張と控除の難しさを招いている
-
- 新しいブラウザは不必要なポリフィルを受け取っている
-
- 1と2によるコードの重複
- 普通のサービスのコードの97%はnpmモジュールによって入れられた外部依存
- また、1000個の外部依存を持っている
- 非常に薄いレイヤーでユニークなアプリケーションを作れているというのは素晴らしいことだが…
- とりあえず webpack-bundle-analyzer を見てみよう
- Reactが2個入ってたりmoment.jsが3個入ってたりしないですか…
- Module と NoModule
- script タグで
type="module"
やnomodule
を指定することでブラウザによって読み込むファイルを切り替えられる - Angularに実装された Differential loading は7~20%のサイズ減少という効果を発揮した
- moduleをサポートしているかどうかでしか切り替えられないので、将来的に同じ問題に立ち向かうことになるんだけどね
- script タグで
- longer term solution を思いついたら私達に連絡してね
- Framework fund に応募してね
Memo
- 自分がある処理を実装するときに Scheduler を使ってまでパフォーマンスを上げたいかと言うと微妙かも知れないけど、様々なライブラリが簡単にスケジュール管理できるようになると考えると、一般のサービス開発者にとってもメリットが大きそう
- OSSをどう改善したかという話かと思ったらWebページのパフォーマンスを向上させるための様々な施策についてデータ込みで語られていて面白かった
- かなり内容の濃い発表だったので消化に時間がかかった
- ようはReactの話なんだけど
- フロントエンド、やらないといけないことが多すぎ!