Magevanta

Magento 2 客戶資料區塊與 localStorage 效能最佳化

每個 Magento 2 店面都使用 客戶資料區塊(customer data sections) —— 這是迷你購物車、顧客姓名顯示、願望清單計數器與結帳摘要背後的機制。對購物者來說看起來很流暢,但實際上可能在不知不覺中拖垮您的頁面載入效能。

如果您曾經好奇為什麼您的頁面在初始載入後會立即發出額外的 AJAX 請求,或者為什麼 localStorage 會膨脹到數 MB,這篇文章就是為您準備的。

什麼是客戶資料區塊?

Magento 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(迷你購物車、訊息等)

這聽起來很有效率,但它有三個主要的效能陷阱

效能陷阱 #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/ 呼叫可能需要 300ms–1.2s,取決於購物車複雜度、商品目錄大小,以及是否已將 Redis 設定為工作階段儲存。

真正的關鍵:它在每個頁面上執行

與購物車/結帳頁面(您預期會有一些後端工作)不同,此請求會在每個快取的 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

這會造成三個問題:

  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

更好的做法是,對於 headless/API-first 架構,完全繞過 customer-data,改用 GraphQL 查詢來取得購物車狀態,只在迷你購物車開啟時載入需要的資料。

6. 使用 Redis 儲存區塊中繼資料

雖然區塊資料本身在瀏覽器中,但後端仍會查詢資料庫。請確保您的 customer/section/load/ 端點使用:

  • Redis 工作階段儲存(而非資料庫工作階段)
  • 透過 Magento_Quote 快取標籤進行 Quote 快取
  • section/load/ 進行 Varnish pass-through(不要快取此端點 —— 它必須是動態的)

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 中儲存數 MB 的資料,並假設所有擴充套件都知道如何負責任地使失效。

解決方法是精準的:稽核您的 sections.xml、移除未使用的區塊、停用您不需要的功能,並確保您的後端工作階段/快取層已針對 customer/section/load/ 端點進行最佳化。

最快的 AJAX 請求,就是您不必發出的那一個。

覺得這篇文章有幫助嗎?請查看我們的 Magento 2 Performance Optimization Guide 2026 以取得完整的店面調整策略。