ResizeObserverエラーを回避する循環参照の防止
Chromiumベースのブラウザで <wa-split-panel> のサイズ変更時に発生していた ResizeObserver エラーが修正されました。この問題は、position と positionInPixels の相互監視による循環参照が原因で、プロパティ変更の条件チェックを追加することで解決されています。
背景
<wa-split-panel> コンポーネントで primary 属性を設定し、プライマリではないパネルが縮小されると、ChromiumベースのブラウザでResizeObserverエラーが発生していました。#2018 で報告されたこの問題は、ブラウザウィンドウを狭めて非プライマリパネルのコンテンツが圧縮される状態で再現します。
再現時には「ResizeObserver loop completed with undelivered notifications」という警告がコンソールに出力されていました。この警告は、ResizeObserverのコールバック内で要素のサイズを変更し、それが次の監視サイクルをトリガーする循環が発生していることを示しています。
技術的な変更
split-panel.ts の handleSizeChange、handlePositionChange、handlePositionInPixelsChange の3つのウォッチャーに条件チェックが追加されました。
handleSizeChangeの変更
変更前:
if (this.primary) {
this.position = this.pixelsToPercentage(this.cachedPositionInPixels);
}
変更後:
if (this.primary) {
const newPosition = this.pixelsToPercentage(this.cachedPositionInPixels);
if (this.position !== newPosition) {
this.position = newPosition;
}
}
handlePositionChangeの変更
変更前:
this.cachedPositionInPixels = this.percentageToPixels(this.position);
this.positionInPixels = this.percentageToPixels(this.position);
変更後:
this.cachedPositionInPixels = this.percentageToPixels(this.position);
const newPositionInPixels = this.percentageToPixels(this.position);
if (this.positionInPixels !== newPositionInPixels) {
this.positionInPixels = newPositionInPixels;
}
handlePositionInPixelsChangeの変更
変更前:
this.position = this.pixelsToPercentage(this.positionInPixels);
変更後:
const newPosition = this.pixelsToPercentage(this.positionInPixels);
if (this.position !== newPosition) {
this.position = newPosition;
}
すべてのウォッチャーで 値の更新前に実際に変更が必要かを確認 するパターンが適用されています。この変更により、パーセンテージとピクセル値の相互変換時に丸め誤差などで値が変わらない場合、不要な更新がスキップされます。
ドキュメントの更新
ドキュメントの変更では、<wa-select> の value="" を削除し、代わりに <wa-option value=""> に selected 属性を追加することで、デフォルト選択状態を明示的にしています。
変更前:
<wa-select label="Primary Panel" value="" style="max-width: 200px; margin-top: 1rem;">
<wa-option value="">None</wa-option>
変更後:
<wa-select label="Primary Panel" style="max-width: 200px; margin-top: 1rem;">
<wa-option value="" selected>None</wa-option>
これにより、デモページの初期状態がより明確になります。
設計判断
プロパティ更新の条件チェックによる循環参照の遮断 という手法が採用されました。
コード内のコメントでは、この修正が「circular watch loop」を防ぐためのものであることが明示されています。position と positionInPixels は相互に依存する関係にあり、一方の変更がもう一方のウォッチャーをトリガーし、それが再び元のウォッチャーをトリガーする可能性がありました。特にResizeObserverのコールバック内でこの循環が発生すると、Chromiumはパフォーマンス保護のために警告を出力します。
値の比較による更新スキップは、ウォッチャーの構造自体を変更せずに循環を断ち切れる最小限の修正といえます。Reactiveなプロパティシステムではよくあるパターンであり、特にChromiumのResizeObserver実装の厳密さに対応するために有効です。
まとめ
本PRは、プロパティ相互参照による循環更新を条件チェックで遮断することで、Chromium特有のResizeObserverエラーを解消しています。3つのウォッチャーすべてに同じパターンを適用することで、一貫性を保ちながらパフォーマンスとブラウザ互換性を向上させた変更です。