Magevanta

Magento 2 Customer Data Sections & localStorage Performance Optimization

Magento 2のストアフロントでは、すべて 顧客データセクション が使用されています。これはミニカート、顧客名表示、ウィッシュリストカウンター、チェックアウトサマリーの背後にある仕組みです。購入者にはシームレスに見えますが、内部ではページ読み込みのパフォーマンスを静かに悪化させている可能性があります。

初期読み込み直後に追加のAJAXリクエストが発生する理由や、localStorageが数メガバイトに膨張する理由について疑問に思ったことがあるなら、この記事はあなたのためのものです。

顧客データセクションとは?

Magento 2はページレンダリングを2つのフェーズに分割しています:サーバーサイド(Astro/Varnish/FPC)とクライアントサイド(JavaScript)です。フルページキャッシュはすべての訪問者に同じHTMLを提供するため、パーソナライズされたデータ(カート内容、ログイン中の顧客名、ウィッシュリスト数)はキャッシュページに対してサーバーサイドでレンダリングできません。

そこで登場するのが sections.xmlCustomer Data JS API です:

<!-- Vendor_Module/etc/frontend/sections.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd">
    <action name="checkout/cart/add">
        <section name="cart"/>
        <section name="checkout-data"/>
    </action>
</config>

Enter fullscreen mode Exit fullscreen mode

このファイルはMagentoに次のことを指示します:checkout/cart/add アクションが実行されたら、cartcheckout-data セクションを無効化する」。次のページ読み込み時に、JavaScriptは無効化されたセクションを検知し、customer/section/load/ AJAX経由で最新データを取得します。

データフロー

  1. ページがFPC/Varnishから読み込まれる(パーソナライズなし)
  2. JSが初期化される Magento_Customer/js/customer-data
  3. localStorageがチェックされる(セクションデータが sectionLoadUrl + storeId キーでキャッシュされているか)
  4. 無効化されたセクションが POST /customer/section/load/ をトリガー(セクション名付き)
  5. レスポンスが localStorage、ko.observables、UI(ミニカート、メッセージなど)を更新

効率的に聞こえますが、3つの大きなパフォーマンスの落とし穴があります。

パフォーマンスの落とし穴 #1: 「セクション地獄」AJAXリクエスト

標準状態では、Magento 2の customer-data モジュールは初回未キャッシュヒット時に 登録されているすべてのセクションcustomer/section/load/ に呼び出します:

// vendor/magento/module-customer/view/frontend/web/js/customer-data.js
// (simplified)
getFromServer: function (sectionNames) {
    return storage.post('customer/section/load/', {
        sections: sectionNames,
        update_section_id: true
    });
}

Enter fullscreen mode Exit fullscreen mode

典型的なMagentoインストールでは、15〜25のセクション が登録されています:cartcheckout-datacomparisondirectory-datacustomerwishlistmessageslast-ordered-itemsreviewproduct_data_storagerecently_viewed_productrecently_compared_productpaypalbilling_agreementpersistent、および拡張機能によって追加されるカスタムセクション。

各セクションはバックエンドでデータベースクエリを必要とします

セクション 典型的なバックエンドコスト
cart Quote読み込み + 商品 + 合計計算
customer 顧客エンティティ読み込み + アドレスコレクション
wishlist ウィッシュリスト商品コレクション
directory-data 国/地域データ + 配送料キャッシュ
checkout-data Quoteアドレス検証 + 配送方法解決

高トラフィックストアでは、この単一の section/load/ 呼び出しがカートの複雑さ、カタログサイズ、Redisがセッションストレージ用に設定されているかどうかによって 300ms〜1.2s かかることがあります。

本当のポイント:すべてのページで実行される

カート/チェックアウトページ(一部のバックエンド処理が予想される場合)とは異なり、このリクエストは すべてのキャッシュされたCMSページ、カテゴリページ、商品ページ で発生します。20商品を閲覧する購入者は 20回のsection/loadリクエスト を生成します — すべて同じ作業を行っています。

パフォーマンスの落とし穴 #2: localStorageの肥大化

セクションデータはブラウザの localStorage に保存されます。Magentoのデフォルトキーフォーマットは次のとおりです:

mage-cache-storage     // ~50KB–500KB+
mage-cache-storage-section-invalidation  // small

Enter fullscreen mode Exit fullscreen mode

cart セクションだけでも、購入者が設定可能商品、カスタムオプション、ティアプライシングを含む複雑なカートを持っている場合、100KB以上 に成長することがあります。20セクションに掛け合わせると、訪問者あたり1〜2MBのlocalStorage になります。

これにより3つの問題が発生します:

  1. モバイルブラウザ はlocalStorageを積極的に削除します。iOS Safariでは、メモリプレッシャー時にストレージがクリアされ、完全な再フェッチを強制されます。
  2. デシリアライズの遅延 — 1MB以上の文字列に対する JSON.parse() はメインレッドを10〜50msブロックし、Interaction to Next Paint (INP)の悪化に寄与します。
  3. セッションストレージクォータ — 一部のブラウザは localStorage を5MBに制限しています。重いカートが制限に近づくと、他の機能が破損する可能性があります。

パフォーマンスの落とし穴 #3: セクション無効化の乱用

最も一般的なミス:すべてのコントローラーアクションでセクション無効化を宣言する

<!-- BAD: Extension invalidates everything on every page -->
<action name="*">
    <section name="cart"/>
    <section name="customer"/>
    <section name="wishlist"/>
</action>

Enter fullscreen mode Exit fullscreen mode

サードパーティ拡張機能の一部はワイルドカード(*)マッチングを使用するか、トラッキングピクセルを介した商品一覧ページビューなどの非カートアクションで cart を無効化します。これにより、何も変更されていない場合でも すべてのページsection/load/ 呼び出しが強制されます。

もう一つの乱用パターン:getSectionData() に対するプラグインで、現在のページに表示されていないデータに対して 高コストなクエリを実行する ことです。

最適化戦略

1. sections.xmlファイルを監査する

まず、実際に無効化している内容をマッピングします:

# Find all section definitions
grep -r "<action name=" app/code vendor/magento --include="sections.xml" | wc -l

# Find wildcard invalidation (highly suspicious)
grep -r '<action name="\*"' app/code vendor --include="sections.xml"

Enter fullscreen mode Exit fullscreen mode

ワイルドカードまたは過度に広範な無効化ごとに、「このアクションは実際にこのセクションのデータを変更するのか?」と問いかけてください。

2. 初期読み込みから未使用セクションを除外する

di.xml を介して初期リクエストで読み込むセクションを設定できます:

<!-- app/etc/di.xml or your module -->
<type name="Magento\Customer\CustomerData\SectionPoolInterface">
    <arguments>
        <argument name="sectionSourceMap" xsi:type="array">
            <!-- Only load what you actually display on every page -->
            <item name="cart" xsi:type="string">Magento\Checkout\CustomerData\Cart</item>
            <item name="customer" xsi:type="string">Magento\Customer\CustomerData\Customer</item>
            <item name="messages" xsi:type="string">Magento\Theme\CustomerData\Messages</item>
        </argument>
    </arguments>
</type>

Enter fullscreen mode Exit fullscreen mode

recently_viewed_productrecently_compared_productreview などのセクションは、初期 section/load/ 呼び出しに含める必要はほとんどありません。関連ウィジェットが初期化されたときに遅延読み込みしてください。

3. 使用しないセクションを無効化する

ウィッシュリストや商品比較を使用していない場合は、完全に削除します:

<!-- app/code/Vendor/Module/etc/frontend/di.xml -->
<type name="Magento\Customer\CustomerData\SectionPool">
    <plugin name="disable_unused_sections"
            type="Vendor\Module\Plugin\DisableUnusedSections"
            sortOrder="10"/>
</type>

Enter fullscreen mode Exit fullscreen mode

<?php
namespace Vendor\Module\Plugin;

class DisableUnusedSections
{
    private array $disabledSections = [
        'wishlist',
        'comparison',
        'review',
        'last-ordered-items'
    ];

    public function afterGetSectionNames(\Magento\Customer\CustomerData\SectionPool $subject, array $result): array
    {
        return array_diff($result, $this->disabledSections);
    }
}

Enter fullscreen mode Exit fullscreen mode

これにより、これらのセクションは section/load/ 呼び出しと localStorage の両方から削除されます。

4. 一時データ用にsessionStorageに切り替える

messages など、タブ間で永続化する必要がないセクションについては、ストレージバックエンドをオーバーライドします:

<!-- app/code/Vendor/Module/etc/frontend/di.xml -->
<type name="Magento\Customer\CustomerData\JsLayoutDataProviderPool">
    <arguments>
        <argument name="components" xsi:type="array">
            <!-- Custom storage handler -->
        </argument>
    </arguments>
</type>

Enter fullscreen mode Exit fullscreen mode

またはよりシンプルなアプローチ:customer-data.js をパッチして、タブ固有のセクションに sessionStorage を使用し、localStorageの圧力を軽減します。

5. サーバーサイドプッシュの実装(上級)

ブラウザがページ読み込みごとにポーリングする代わりに、データが実際に変更されたときのみ更新をプッシュします:

// In your observer, after cart modification
$sectionIdentifier = $this->sectionIdentifierFactory->create();
$sectionIdentifier->resetIdentifier(); // Forces JS to re-fetch on next action

Enter fullscreen mode Exit fullscreen mode

より良い方法として、ヘッドレス/API優先アーキテクチャでは customer-data を完全にバイパスし、ミニカートが開かれたときに必要なものだけを読み込む GraphQLクエリ を使用します。

6. セクションメタデータ用にRedisを使用

セクションデータ自体はブラウザにありますが、バックエンドは依然としてデータベースをクエリします。customer/section/load/ エンドポイントが以下を使用していることを確認してください:

  • Redisセッションストレージ(DBセッションではない)
  • Magento_Quoteキャッシュタグを介したQuoteキャッシング
  • section/load/ 用のVarnishパススルー(このエンドポイントをキャッシュしない — 動的である必要があります)

7. 前後の測定

BlackfireまたはNew Relicを使用して customer/section/load/ 呼び出しをトレースします:

  1. 任意のキャッシュされた商品ページを開く
  2. ネットワーク タブを確認 — section/load/ POSTを見つける
  3. プロファイル — QuoteRepository::getTotalsCollector::collectAddressRepository::getList を探す
  4. 最適化を適用して再プロファイル

最適化後の予想結果:

メトリック
セクション数 22 6
AJAXペイロードサイズ 45KB 8KB
section/load/ バックエンド時間 450ms 80ms
localStorageサイズ 1.2MB 180KB
INP改善 -40ms

まとめ

顧客データセクションは強力ですが危険な機能です。デフォルトのMagento 2設定は 寛大すぎる — すべてのページですべてのセクションを読み込み、メガバイト単位のデータをlocalStorageに保存し、すべての拡張機能が責任を持って無効化する方法を知っていることを前提としています。

解決策は外科的手術的です:sections.xml を監査し、未使用のセクションを削除し、必要ない機能を無効化し、customer/section/load/ エンドポイント向けにバックエンドセッション/キャッシュレイヤーが最適化されていることを確認します。

最も高速なAJAXリクエストは、実行する必要のないリクエストです。

役に立ったと思ったら、ストアフロント全体のチューニング戦略については Magento 2 Performance Optimization Guide 2026 をご覧ください。