Angular v17のDeferrable Viewsと内部実装の話
Deferrable Viewsの話
Angular v17の新機能
@defer ブロックで囲われたビューに含まれるコンポーネントは自動的にチャンク分割され、遅延読み込みされる。
初期バンドルサイズの削減により読み込み時間の短縮に効果あり
code:html
@defer {
<large-component />
}
遅延読み込みを開始するためのいろいろなトリガーがある
code:html
@defer (on TRIGGER) {
<large-component />
}
各トリガーはどのように実装されているのか?
on interaction
click と keydown イベントをリッスンしている。
つまりマウスとキーボードの両対応
code:ts
/** Names of the events considered as interaction events. */
// ...
for (const name of interactionEventNames) {
trigger.addEventListener(name, entry!.listener, eventListenerOptions);
}
on hover
mouseenter と focusin イベントをリッスンしている。
つまりマウスとキーボードの両対応
code:ts
/** Names of the events considered as hover events. */
for (const name of hoverEventNames) {
trigger.addEventListener(name, entry!.listener, eventListenerOptions);
}
on viewport
少し複雑だが、結局 IntersectionObserver を使っている。
isIntersecting した要素が viewport トリガーのターゲット要素だったら発火する。
code:ts
intersectionObserver = intersectionObserver || ngZone.runOutsideAngular(() => {
return new IntersectionObserver(entries => {
for (const current of entries) {
// Only invoke the callbacks if the specific element is intersecting.
if (current.isIntersecting && viewportTriggers.has(current.target)) {
ngZone.run(viewportTriggers.get(current.target)!.listener);
}
}
});
});
if (!entry) {
entry = new DeferEventEntry();
ngZone.runOutsideAngular(() => intersectionObserver!.observe(trigger));
viewportTriggers.set(trigger, entry);
observedViewportElements++;
}
on idle
詳細は割愛するが、ブラウザが requestIdleCallback をサポートしていれば使い、サポートされてなければ setTimeout にフォールバックしている。
相変わらずSafari系では実装されていないため、idle トリガーの振る舞いはSafariとそれ以外で変わる可能性がある。
code:ts
const _requestIdleCallback = () =>
typeof requestIdleCallback !== 'undefined' ? requestIdleCallback : setTimeout;
const _cancelIdleCallback = () =>
typeof requestIdleCallback !== 'undefined' ? cancelIdleCallback : clearTimeout;
export function onIdle(callback: VoidFunction, lView: LView) {
const scheduler = injector.get(IdleScheduler);
const cleanupFn = () => scheduler.remove(callback);
scheduler.add(callback);
return cleanupFn;
}
on timer
結構複雑にタイマーが実装されている
負荷がかかりすぎないように、60fpsよりも短いスパンにならないよう調整されている。
つまり、timer トリガーで16msより短い指定はできない。
まとめ
@defer はいいぞ
いまどきのフレームワークの機能はデフォルトでアクセシビリティが考慮されている。
requestIdleCallback を早く普通に使いたい。