概要
近年のWebサイトにはリッチなコンテンツが欠かせません。特に、メインビジュアルなどで画像スライダーを導入する例は少なくないと思います。しかし、これらの機能が原因で、特に古いスマートフォンなどのデバイスでサイトがクラッシュする、といった思わぬ問題に直面することがありました😭
本記事では、実際に発生した画像スライダーのクラッシュ事例を基に、その裏で何が起こっていたのか、そして「仮想化」技術がどのようにこの問題を解決したのかを深掘りします。
実例紹介
問題の発生: 数百枚の画像がある巨大スライダーが引き起こしたクラッシュ 💥
とあるWebサイトの運用時に、以下のような状況でブラウザがクラッシュしてしまう問題が発生しました。
- ECサイトのメインビジュアル: 商品画像を表示するスライダーが導入されていました。
- 実装ライブラリ: スライダーの実装にはSwiperを使用。
- コンテンツ量: 商品によっては300枚を超える画像がスライダーに含まれていました。
- 画像解像度: 各画像は1500x1800ピクセルと非常に高解像度でした(画像の拡大機能もあったため高解像度である必要があった)
- 問題発生環境: 特に古いモバイルSafariにおいて、画面着地後にブラウザがクラッシュすることがありました📱💥
根本の原因
このクラッシュの根本原因は、まさに高解像度の画像が大量に読み込まれることによるメモリの過剰消費でした😭
1500x1800ピクセルのRGBA画像は、1枚あたり約10MBのメモリを必要とします。これが300枚あると単純計算で、理論上は以下のメモリ使用量となります。
1500px * 1800px * 4bite(1pxあたりの使用量) * 300 = 3,240,000,000bite ≒ 3GB
この膨大なメモリ使用量が、特に物理メモリの少ない古いモバイル端末において、メモリ不足によるクラッシュを引き起こしていたと考えられます😱
解決策
このクラッシュ問題に対して「swiperのVirtual SlidesオプションをONにする」ことで解決できました。swiperのVirtual Slidesオプションは、スライダーをいわゆる「仮想化」するためのオプションとなっており、現在表示中のスライドの要素と、その前後数枚分の要素のみがDOMに描画されるようになります。
これによってimgタグが大量にDOMツリーに描画されることがなくなり(= 画像データのキャッシュも抑制される)、クラッシュを防止することができました。
裏でどんな処理が起こっている...?
クラッシュの根本原因はどうやら、ブラウザのメモリ管理にあるようでした。これを理解するためにWebページがロードされ、imgタグが描画されるまでにどのような処理が行われているのかを知る必要がありそうです👀
DOM(Document Object Model)の構築と画像リクエスト
WebブラウザはHTMLファイルを読み込むと、まずDOMツリーを構築します。この際、<img src="image.jpg">
のような<img>
タグが見つかると、src
属性に指定された画像ファイルをネットワーク経由でリクエストします。
画像データのダウンロード、デコード、そしてメモリへの保持
サーバーから画像データがダウンロードされると、ブラウザはそれを表示可能なピクセルデータ(ビットマップ)にデコード(圧縮解除)します。このデコードされたデータは、元の画像ファイルよりもはるかに多くのメモリを消費します。
デコードされた画像データは、Webページの描画を高速化するため、そしてユーザーがスクロールしたりタブを切り替えたりしてもすぐに表示できるように、ブラウザの内部メモリにキャッシュとして保持されます。これが貯まったり一度に処理したりすることで、メモリの過剰な蓄積につながります。
まとめ
大量のメディアコンテンツをウェブページで扱う際に、「仮想化」という技術が非常に有効です。画像スライダー、無限スクロール、データ量の多いテーブルやリストなどで特にその威力を発揮します。
仮想化が必要な理由
すべてのコンテンツをDOM(Document Object Model)にレンダリングしてしまうと、特にリソースが限られたデバイスでは、ページのクラッシュやパフォーマンス低下を招く可能性があります。
例えば、SNSのタイムラインのような無限スクロールでは、スクロールするたびに新しいコンテンツが読み込まれますが、過去に表示したコンテンツをすべてDOMに残しておくと、以下の問題が発生します。
- DOMツリーの肥大化: スクロールするたびにDOM要素が増え続け、ページのパフォーマンスが低下します。
- メモリの蓄積: 表示された画像や動画、その他のデータがメモリに蓄積され、最終的にデバイスのメモリを圧迫します。
仮想化は、現在ビューポート(画面)に表示されているコンテンツのみをDOMに保持し、画面外にスクロールしたコンテンツはDOMから削除するという考え方です。これにより、メモリとDOMツリーのサイズを常に最適に保ち、ユーザーにスムーズな体験を提供できます。
この「仮想化」に関してはもちろんフルスクラッチで実装することも可能ですが、react-windowやreact-virtualized、react-virtuosoといったライブラリを使用することで簡単に実装することも可能です👀
Webパフォーマンスとユーザー体験の向上のためにも、大量コンテンツを扱う際には「仮想化」の概念を積極的に取り入れましょう!