عملکرد بلاک‌های وردپرس می‌تواند به دلایل متعددی سایت را کند کند: بارگذاری CSS و JavaScript اضافی، رندر سمت سرور ناکارآمد، کوئری‌های تکراری، Hydration سنگین در ویرایشگر، و نبود Lazy Loading. هر بلاک گوتنبرگ در فرانت‌اند به HTML، CSS و در برخی موارد JavaScript تبدیل می‌شود و اگر این سه لایه بهینه نباشند، مجموع آن‌ها می‌تواند زمان بارگذاری صفحه را چند برابر کند. برخلاف تصور رایج، مشکل همیشه از تعداد بلاک‌ها نیست؛ کیفیت پیاده‌سازی هر بلاک تعیین‌کننده است. یک صفحه با پنج بلاک سنگین می‌تواند کندتر از صفحه‌ای با پنجاه بلاک سبک باشد. این مقاله ریشه‌های فنی کندی ناشی از بلاک‌ها را تحلیل می‌کند و چارچوب عملی بهینه‌سازی را ارائه می‌دهد.

در یک پروژه فروشگاهی، تیم محتوا از یک افزونه بلاک شخص ثالث برای نمایش محصولات استفاده می‌کرد. صفحه اصلی با ۱۲ بلاک، در Lighthouse نمره ۴۲ می‌گرفت. بعد از تحلیل، مشخص شد آن یک بلاک، ۴۵۰ کیلوبایت JavaScript اضافه بارگذاری می‌کند که در تمام صفحات سایت لود می‌شد. حذف آن بلاک و جایگزینی با Query Loop بومی، نمره را به ۹۴ رساند. این تجربه نشان می‌دهد که کندی سایت، اغلب از یک بلاک خاص و نه از تعداد بلاک‌ها ناشی می‌شود.

چرا بلاک‌ها می‌توانند سایت را کند کنند؟

بلاک‌های گوتنبرگ (Gutenberg Blocks) در فرانت‌اند به سه لایه تبدیل می‌شوند: HTML برای ساختار، CSS برای استایل، و JavaScript برای تعامل. هر لایه، پتانسیل کندی دارد. HTML اضافی، حجم DOM (Document Object Model) را افزایش می‌دهد. CSS اضافی، زمان Render Blocking را بیشتر می‌کند. JavaScript اضافی، زمان Parse و Execute را افزایش می‌دهد. مجموع این سه، تجربه کاربری را تحت تأثیر قرار می‌دهد.

اگر با چرا قالب‌های بلاکی آینده وردپرس هستند آشنا شده باشید، می‌دانید که معماری بلاکی یک تغییر پارادایم است. اما این پارادایم، اگر بدون رعایت اصول عملکردی پیاده شود، می‌تواند به یک بدهی فنی بزرگ تبدیل گردد. برخلاف قالب‌های کلاسیک که ساختار HTML در PHP تعریف می‌شد، در بلاک تم‌ها ساختار از دیتابیس خوانده می‌شود و همین، امکان ایجاد ساختارهای ناکارآمد را افزایش می‌دهد.

«هر بلاک، یک قرارداد است: در ازای انعطاف‌پذیری طراحی، هزینه‌ای بر عملکرد تحمیل می‌کند. اگر این هزینه مدیریت نشود، انعطاف‌پذیری به بدهی تبدیل می‌شود.»

سه منبع اصلی کندی ناشی از بلاک‌ها:

منبع لایه تأثیر شاخص کلیدی
CSS و JS اضافی شبکه و Parse LCP، TBT
کوئری‌های تکراری دیتابیس و سرور TTFB
DOM سنگین رندر و Layout CLS، INP

نکته مهم این است که وردپرس از نسخه ۵.۸ به بعد، یک ویژگی به‌نام Block Asset Loading معرفی کرد که فقط CSS و JS بلاک‌هایی را بارگذاری می‌کند که در صفحه استفاده شده‌اند. این ویژگی، تفاوت محسوسی در عملکرد ایجاد کرد، اما در صورتی که بلاک‌ها به‌درستی ثبت شده باشند. اگر یک بلاک، Assetهای خود را به‌صورت دستی Enqueue کند، این ویژگی کار نمی‌کند.

بارگذاری CSS و JavaScript اضافی

بزرگ‌ترین عامل کندی در سایت‌های بلاکی، بارگذاری Assetهای غیرضروری است. هر بلاک می‌تواند سه فایل داشته باشد: style-index.css برای فرانت‌اند، index.css برای ویرایشگر، و index.js برای تعامل. اگر بلاک به‌درستی ثبت شود، وردپرس فقط فایل‌های بلاک‌های استفاده‌شده را بارگذاری می‌کند. اما در عمل، چند مشکل رایج وجود دارد.

مشکل اول، ثبت دستی Assetها است. اگر توسعه‌دهنده به‌جای استفاده از block.json، Assetها را در functions.php با wp_enqueue_script() ثبت کند، وردپرس نمی‌تواند تشخیص دهد که این Assetها فقط برای یک بلاک خاص هستند و آن‌ها را در تمام صفحات بارگذاری می‌کند. این الگو، یکی از شایع‌ترین اشتباهات در توسعه بلاک است.

مشکل دوم، استفاده از کتابخانه‌های سنگین است. اگر بلاک شما از یک کتابخانه UI مثل Material-UI یا Ant Design استفاده می‌کند، حجم JavaScript آن می‌تواند به چند صد کیلوبایت برسد. وردپرس یک اکوسیستم کامپوننت به‌نام @wordpress/components دارد که سبک‌تر و بهینه‌تر است. اگر با بهینه‌سازی جاوااسکریپت و کاهش حجم آن آشنا شده باشید، این انتخاب را به‌عنوان یک تصمیم معمارانه می‌شناسید.

مشکل سوم، عدم Tree Shaking است. اگر بلاک شما از یک کتابخانه بزرگ استفاده می‌کند اما فقط بخش کوچکی از آن را نیاز دارد، بدون Tree Shaking کل کتابخانه در Bundle نهایی قرار می‌گیرد. ابزار @wordpress/scripts به‌طور پیش‌فرض Tree Shaking را فعال می‌کند، اما اگر پیکربندی سفارشی Webpack داشته باشید، ممکن است این ویژگی غیرفعال شود.

«هر کیلوبایت JavaScript اضافی، یک میلی‌ثانیه زمان Parse در موبایل متوسط است. اگر بلاک شما ۵۰۰ کیلوبایت JS اضافه کند، کاربران موبایل نیم ثانیه فقط برای Parse منتظر می‌مانند.»

راه‌حل عملی: در block.json، فقط Assetهای ضروری را تعریف کنید. از editorScript برای JavaScript ویرایشگر، editorStyle برای CSS ویرایشگر، و style برای CSS فرانت‌اند استفاده کنید. اگر بلاک شما در فرانت‌اند JavaScript نیاز ندارد (بلاک استاتیک)، فایل viewScript را تعریف نکنید:

{
  "name": "my-plugin/static-block",
  "editorScript": "file:./index.js",
  "editorStyle": "file:./index.css",
  "style": "file:./style-index.css"
}

در این مثال، بلاک فقط در ویرایشگر JavaScript بارگذاری می‌کند و در فرانت‌اند فقط CSS دارد. این الگو، برای بلاک‌های استاتیک مثل Heading، Paragraph، و Image ایده‌آل است.

نقش ثبت بلاک در عملکرد

روش ثبت بلاک، تأثیر مستقیمی بر عملکرد سایت دارد. سه روش ثبت وجود دارد و هرکدام پیامدهای متفاوتی دارند.

روش اول: ثبت با block.json. این روش استاندارد و توصیه‌شده است. وردپرس به‌طور خودکار Assetها را مدیریت می‌کند، فقط Assetهای بلاک‌های استفاده‌شده را بارگذاری می‌نماید، و Metadata را در یک فایل متمرکز نگه می‌دارد. اگر با ساخت بلاک سفارشی گوتنبرگ از صفر آشنا شده باشید، می‌دانید که این روش، استاندارد فعلی است.

روش دوم: ثبت با registerBlockType در JavaScript. این روش قدیمی‌تر است و هنوز پشتیبانی می‌شود، اما Assetها را به‌صورت دستی مدیریت می‌کند. اگر Assetها در functions.php ثبت شوند، وردپرس نمی‌تواند Lazy Loading بلاک‌ها را اعمال کند.

روش سوم: ثبت با register_block_type در PHP. این روش برای بلاک‌های داینامیک (که سمت سرور رندر می‌شوند) استفاده می‌شود. اگر با پارامتر render_callback پیاده‌سازی شود، می‌تواند بهینه باشد. اما اگر render_callback کوئری‌های سنگین اجرا کند، می‌تواند TTFB را افزایش دهد.

روش ثبت Lazy Loading پیچیدگی توصیه
block.json بومی پایین استاندارد
registerBlockType (JS) دستی متوسط فقط برای موارد خاص
register_block_type (PHP) وابسته به پیاده‌سازی بالا برای بلاک داینامیک

نکته کلیدی: اگر بلاک شما با block.json ثبت شده و از پارامتر render برای رندر سمت سرور استفاده می‌کند، وردپرس می‌تواند Assetها را به‌صورت دقیق مدیریت کند. این الگو، ترکیبی از بهینه‌سازی سمت کلاینت و سمت سرور است.

بلاک‌های داینامیک و رندر سمت سرور

بلاک‌های داینامیک، آن‌هایی هستند که محتوای خود را در زمان درخواست از سرور دریافت می‌کنند، نه در زمان ذخیره. مثال‌های رایج: بلاک آخرین نوشته‌ها، بلاک محصولات فروشگاه، بلاک نظرات. این بلاک‌ها در دیتابیس فقط Attributeها را ذخیره می‌کنند و HTML در هر درخواست تولید می‌شود.

مزیت بلاک‌های داینامیک، تازگی محتوا است. عیب آن‌ها، هزینه پردازش در هر درخواست است. اگر یک بلاک داینامیک، در هر بارگذاری صفحه یک کوئری سنگین اجرا کند، TTFB (Time to First Byte) افزایش می‌یابد. اگر با TTFB و روش‌های کاهش آن آشنا شده باشید، می‌دانید که این شاخص، مستقیماً بر LCP (Largest Contentful Paint) اثر می‌گذارد.

سه الگوی ناکارآمد در بلاک‌های داینامیک:

الگوی اول: کوئری در render_callback بدون Cache. اگر render_callback در هر درخواست یک WP_Query اجرا کند و نتیجه را کش نکند، بار دیتابیس چند برابر می‌شود. راه‌حل: استفاده از Transient API برای کش نتیجه:

function my_block_render_callback( $attributes ) {
    $cache_key = 'my_block_' . md5( serialize( $attributes ) );
    $cached = get_transient( $cache_key );

    if ( false !== $cached ) {
        return $cached;
    }

    $query = new WP_Query( array(
        'posts_per_page' => 5,
        'no_found_rows'  => true,
    ) );

    $output = '<ul>';
    while ( $query->have_posts() ) {
        $query->the_post();
        $output .= '<li>' . esc_html( get_the_title() ) . '</li>';
    }
    $output .= '</ul>';

    set_transient( $cache_key, $output, HOUR_IN_SECONDS );
    return $output;
}

پارامتر no_found_rows در WP_Query باعث می‌شود که وردپرس کوئری SQL_CALC_FOUND_ROWS را اجرا نکند. این کوئری، برای صفحه‌بندی لازم است اما اگر بلاک شما صفحه‌بندی ندارد، غیرضروری است و می‌تواند کوئری را چند برابر کند.

الگوی دوم: N+1 Query Problem. اگر render_callback در یک حلقه foreach، برای هر آیتم یک کوئری جداگانه اجرا کند، تعداد کوئری‌ها به تعداد آیتم‌ها ضرب می‌شود. راه‌حل: استفاده از WP_Query با post__in یا update_post_meta_cache() برای پرکردن کش متادیتا در یک کوئری.

الگوی سوم: عدم استفاده از Object Cache. اگر Object Cache (مثل Redis یا Memcached) فعال نباشد، هر کوئری به دیتابیس می‌رود. فعال‌سازی Object Cache، به‌خصوص در بلاک‌های داینامیک، تفاوت محسوسی در عملکرد ایجاد می‌کند. اگر با بهینه‌سازی کوئری‌های وردپرس با کدنویسی آشنا شده باشید، این الگوها برای شما آشناست.

Query Loop و کوئری‌های تکراری

بلاک Query Loop یکی از پرکاربردترین و در عین حال پرمصرف‌ترین بلاک‌های گوتنبرگ است. این بلاک، امکان نمایش لیست نوشته‌ها، محصولات، یا هر Post Type دیگری را فراهم می‌کند. اما اگر به‌درستی پیکربندی نشود، می‌تواند منبع اصلی کندی باشد.

مشکل اصلی Query Loop، اجرای کوئری در هر بارگذاری صفحه است. برخلاف بلاک‌های استاتیک که HTML آن‌ها در دیتابیس ذخیره می‌شود، Query Loop در هر درخواست یک WP_Query جدید اجرا می‌کند. اگر صفحه شما شامل چند Query Loop باشد (مثلاً یکی برای آخرین نوشته‌ها و یکی برای محصولات پرطرفدار)، تعداد کوئری‌ها چند برابر می‌شود.

<!-- wp:query {"query":{"perPage":6,"postType":"post","order":"desc","orderBy":"date"}} -->
<div class="wp-block-query">
    <!-- wp:post-template -->
    <!-- wp:post-title /-->
    <!-- wp:post-featured-image /-->
    <!-- wp:post-excerpt /-->
    <!-- /wp:post-template -->
</div>
<!-- /wp:query -->

این Query Loop ساده، یک کوئری به جدول wp_posts و یک کوئری به wp_postmeta برای هر نوشته اجرا می‌کند. اگر ۶ نوشته نمایش داده شود، ممکن است ۷ تا ۱۰ کوئری اجرا شود.

سه راه‌حل برای بهینه‌سازی Query Loop:

راه‌حل اول: کاهش تعداد آیتم‌ها. تعداد perPage را به حداقل لازم کاهش دهید. اگر ۳ نوشته کافی است، ۶ نوشته نمایش ندهید.

راه‌حل دوم: غیرفعال کردن صفحه‌بندی در صورت عدم نیاز. اگر Query Loop صفحه‌بندی ندارد، از no_found_rows استفاده کنید تا کوئری SQL_CALC_FOUND_ROWS حذف شود.

راه‌حل سوم: استفاده از Transient API برای کش. اگر Query Loop محتوای نسبتاً ثابتی دارد، نتیجه را در Transient ذخیره کنید. اگر با کش هوشمند با Transient API آشنا شده باشید، این الگو برای شما آشناست.

«Query Loop یک شمشیر دو لبه است: از یک سو، امکان ساخت صفحات پویا را بدون کدنویسی فراهم می‌کند. از سوی دیگر، اگر بدون مدیریت Cache استفاده شود، بار دیتابیس را چند برابر می‌کند.»

نکته پیشرفته: از وردپرس ۶.۱ به بعد، Query Loop از پارامتر queryId پشتیبانی می‌کند که به شما اجازه می‌دهد نتایج کوئری را بین چند بلاک مشترک کنید. این ویژگی، Query Sharing نامیده می‌شود و می‌تواند تعداد کوئری‌ها را به‌شدت کاهش دهد:

<!-- wp:query {"queryId":1,"query":{"perPage":6}} -->
<!-- wp:query {"queryId":1,"query":{"perPage":6,"offset":6}} -->

هزینه Hydration در ویرایشگر و فرانت‌اند

Hydration فرآیندی است که در آن، HTML استاتیک با JavaScript زنده می‌شود و قابلیت تعامل پیدا می‌کند. در بلاک‌های گوتنبرگ، Hydration دو لایه دارد: Hydration در ویرایشگر (که همیشه فعال است) و Hydration در فرانت‌اند (که فقط برای بلاک‌های تعاملی فعال است).

در ویرایشگر، هر بلاک یک کامپوننت React است که Hydrate می‌شود. اگر بلاک شما از کتابخانه‌های سنگین استفاده کند، بارگذاری ویرایشگر کند می‌شود. این مشکل، مستقیماً بر تجربه تیم محتوا اثر می‌گذارد و می‌تواند بهره‌وری را کاهش دهد.

در فرانت‌اند، Hydration فقط برای بلاک‌هایی که viewScript دارند فعال است. اگر بلاک شما تعاملی است (مثل Tab، Accordion، Slider)، Hydration ضروری است. اما اگر بلاک شما استاتیک است و viewScript دارد، Hydration اضافی است و باید حذف شود.

سه روش برای کاهش هزینه Hydration:

روش اول: Interactive API. وردپرس از نسخه ۶.۵ یک API جدید به‌نام Interactivity API معرفی کرده که به‌جای Hydration کامل، فقط بخش‌های تعاملی را زنده می‌کند. این API، مشابه Islands Architecture در Astro است و می‌تواند هزینه JavaScript را به‌شدت کاهش دهد:

import { store, getContext } from '@wordpress/interactivity';

store( 'my-plugin', {
    actions: {
        toggle: () => {
            const context = getContext();
            context.isOpen = ! context.isOpen;
        },
    },
} );

روش دوم: Progressive Enhancement. اگر بلاک شما به‌صورت پایه با HTML کار می‌کند (مثلاً با <details> برای Accordion)، نیازی به JavaScript ندارد. اگر با بهینه‌سازی HTML و کاهش حجم آن آشنا شده باشید، این رویکرد را به‌عنوان یک اصل می‌شناسید.

روش سوم: Defer و Async. اگر بلاک شما JavaScript دارد، باید با defer یا async بارگذاری شود تا Render Blocking را کاهش دهد. وردپرس از نسخه ۶.۳ به بعد، این ویژگی را برای Scriptهای بلاک به‌طور خودکار اعمال می‌کند.

تصاویر و رسانه در بلاک‌ها

تصاویر، بزرگ‌ترین عامل کندی در سایت‌های بلاکی هستند. بلاک Image، بلاک Cover، و بلاک Gallery می‌توانند تصاویر حجیم را در صفحه بارگذاری کنند که مستقیماً بر LCP اثر می‌گذارد. اگر با Core Web Vitals و اهمیت آن آشنا شده باشید، می‌دانید که LCP یکی از سه معیار اصلی گوگل است.

سه مشکل رایج در تصاویر بلاک‌ها:

مشکل اول: عدم استفاده از srcset. اگر بلاک Image از wp_get_attachment_image() استفاده نکند و تصویر را با <img src="..."> ساده نمایش دهد، مرورگر نمی‌تواند اندازه مناسب را انتخاب کند. نتیجه: تصویر ۲۰۰۰ پیکسلی در موبایل بارگذاری می‌شود. راه‌حل: استفاده از توابع وردپرس که به‌طور خودکار srcset و sizes تولید می‌کنند.

مشکل دوم: عدم Lazy Loading. اگر تصاویر پایین صفحه با loading="lazy" بارگذاری نشوند، همه آن‌ها در زمان بارگذاری اولیه دانلود می‌شوند. وردپرس از نسخه ۵.۵ به بعد، این ویژگی را به‌طور خودکار اضافه می‌کند، اما اگر بلاک شما HTML سفارشی تولید کند، ممکن است این ویژگی اعمال نشود.

مشکل سوم: فرمت‌های قدیمی. اگر تصاویر با فرمت JPEG یا PNG بارگذاری شوند، حجم آن‌ها چند برابر WebP یا AVIF است. اگر با بهترین فرمت تصویر برای وب آشنا شده باشید، می‌دانید که WebP می‌تواند حجم را ۳۰ تا ۵۰ درصد کاهش دهد.

مشکل تأثیر بر CWV راه‌حل
عدم srcset LCP wp_get_attachment_image()
عدم Lazy Loading LCP، TBT loading="lazy"
فرمت قدیمی LCP WebP یا AVIF
عدم Width/Height CLS تعریف ابعاد صریح

نکته مهم در بلاک Cover و Gallery: این بلاک‌ها معمولاً تصاویر بزرگ را به‌عنوان Background بارگذاری می‌کنند. اگر این تصاویر بهینه نباشند، LCP به‌شدت افزایش می‌یابد. راه‌حل: استفاده از تصاویر WebP، تعریف fetchpriority="high" برای تصویر LCP، و پیش‌بارگذاری (<link rel="preload">) برای تصویر Hero.

تأثیر theme.json بر حجم CSS

فایل theme.json قلب تنظیمات بلاک تم‌ها است، اما اگر به‌درستی پیکربندی نشود، می‌تواند حجم CSS تولیدشده را چند برابر کند. وردپرس از این فایل، یک فایل CSS به‌نام global-styles.css تولید می‌کند که در هر صفحه بارگذاری می‌شود.

سه عامل افزایش حجم global-styles.css:

عامل اول: تعداد زیاد Presetها. هر رنگ، فونت، و فاصله در theme.json به یک CSS Variable تبدیل می‌شود که در :root تعریف می‌گردد. اگر ۵۰ رنگ تعریف کنید، ۵۰ CSS Variable در :root اضافه می‌شود که در هر صفحه بارگذاری می‌شوند.

عامل دوم: تعریف Styles برای بلاک‌های زیاد. هر بلاک که در theme.json استایل می‌گیرد، یک بلوک CSS جداگانه تولید می‌کند. اگر ۳۰ بلاک را استایل کنید، فایل CSS می‌تواند به چند صد کیلوبایت برسد.

عامل سوم: Duplication. اگر یک مقدار در چند جا تعریف شود، وردپرس ممکن است آن را چند بار در CSS تولید کند. این مشکل، به‌خصوص در theme.jsonهای پیچیده رخ می‌دهد.

راه‌حل: در theme.json، فقط Presetهای ضروری را تعریف کنید. برای استایل‌های بلاک، به‌جای تعریف در theme.json، از styles.css استفاده کنید. اگر با بهینه‌سازی CSS و کاهش حجم آن آشنا شده باشید، این رویکرد را به‌عنوان یک اصل می‌شناسید.

«theme.json یک فایل تنظیمات است، نه یک فایل استایل. هرچه بیشتر در آن تعریف کنید، حجم CSS تولیدشده بیشتر می‌شود. تعادل را حفظ کنید.»

نکته پیشرفته: وردپرس از نسخه ۶.۶ پارامتری به‌نام useRootPaddingAwareAlignments معرفی کرد که به‌طور خودکار Padding را در theme.json مدیریت می‌کند. اگر این پارامتر فعال باشد، حجم CSS تولیدشده کاهش می‌یابد چون نیازی به تعریف Padding در چند جا نیست:

{
  "settings": {
    "useRootPaddingAwareAlignments": true,
    "layout": {
      "contentSize": "720px",
      "wideSize": "1200px"
    }
  }
}

بلاک‌های شخص ثالث و بدهی فنی

بلاک‌های شخص ثالث، بزرگ‌ترین منبع نامشخص در عملکرد سایت‌های بلاکی هستند. اگر با تأثیر افزونه‌های وردپرس بر سرعت سایت آشنا شده باشید، می‌دانید که هر افزونه می‌تواند بار اضافی ایجاد کند. اما بلاک‌های شخص ثالث، چند برابر خطرناک‌تر هستند چون: در هر صفحه بارگذاری می‌شوند، می‌توانند Assetهای خود را در تمام سایت Enqueue کنند، و ممکن است با بلاک‌های دیگر تداخل داشته باشند.

سه مشکل رایج در بلاک‌های شخص ثالث:

مشکل اول: بارگذاری Assetهای اضافی. برخی افزونه‌های بلاک، Assetهای خود را در wp_enqueue_scripts به‌جای block.json ثبت می‌کنند. نتیجه: Assetهای آن‌ها در تمام صفحات سایت بارگذاری می‌شود، حتی اگر بلاک در آن صفحه استفاده نشده باشد.

مشکل دوم: استفاده از کتابخانه‌های قدیمی. برخی بلاک‌ها از نسخه‌های قدیمی jQuery یا کتابخانه‌های مشابه استفاده می‌کنند که بار اضافی دارند. اگر با شناسایی افزونه‌های اضافی وردپرس آشنا شده باشید، این موضوع را به‌عنوان یک بدهی فنی می‌شناسید.

مشکل سوم: عدم به‌روزرسانی منظم. برخی بلاک‌ها با نسخه‌های جدید گوتنبرگ سازگار نیستند و ممکن است خطاهای JavaScript ایجاد کنند که بر کل صفحه اثر می‌گذارد.

راه‌حل عملی: قبل از نصب هر بلاک شخص ثالث، سه چیز را بررسی کنید. اول، تعداد نصب فعال آن در مخزن وردپرس. دوم، آخرین تاریخ به‌روزرسانی. سوم، تست عملکرد با Query Monitor یا ابزارهای مشابه. اگر بلاک در صفحه‌ای که استفاده نشده، Asset بارگذاری می‌کند، آن بلاک مشکل‌دار است و باید جایگزین شود.

اندازه‌گیری و ابزارهای عیب‌یابی

بدون اندازه‌گیری دقیق، بهینه‌سازی عملکرد یک فرآیند کور است. چند ابزار کلیدی برای عیب‌یابی عملکرد بلاک‌ها:

ابزار اول: Query Monitor. این افزونه، کوئری‌های دیتابیس، Hookها، و تماس‌های HTTP را نشان می‌دهد. اگر با پروفایلینگ عملکرد وردپرس با Query Monitor آشنا شده باشید، می‌دانید که این ابزار، اولین گام در عیب‌یابی است.

ابزار دوم: Chrome DevTools Performance Panel. این پنل، زمان Parse، Execute، و Render را نشان می‌دهد. با استفاده از این پنل، می‌توانید ببینید کدام بلاک بیشترین زمان JavaScript را مصرف می‌کند.

ابزار سوم: Lighthouse. این ابزار، نمره کلی عملکرد را با جزئیات LCP، TBT، و CLS می‌دهد. برای تحلیل دقیق، باید هر معیار را جداگانه بررسی کنید.

ابزار چهارم: WebPageTest. این ابزار، Waterfall کامل بارگذاری صفحه را با جزئیات در سطح شبکه نشان می‌دهد. با استفاده از این ابزار، می‌توانید ببینید کدام فایل CSS یا JS از کدام بلاک می‌آید و چه تأثیری بر زمان بارگذاری دارد.

ابزار تمرکز نوع
Query Monitor دیتابیس و Hook افزونه وردپرس
DevTools Performance JavaScript و Render مرورگر
Lighthouse CWV کلی مرورگر یا آنلاین
WebPageTest Waterfall شبکه آنلاین
PageSpeed Insights CWV + توصیه‌ها آنلاین

نکته پیشرفته: برای تحلیل دقیق، از Performance Observer API در مرورگر استفاده کنید. این API به شما اجازه می‌دهد که زمان LCP، INP، و CLS را به‌صورت دقیق اندازه‌گیری کنید و در Google Analytics گزارش دهید:

new PerformanceObserver( ( list ) => {
    for ( const entry of list.getEntries() ) {
        if ( entry.entryType === 'largest-contentful-paint' ) {
            console.log( 'LCP:', entry.startTime );
        }
    }
} ).observe( { type: 'largest-contentful-paint', buffered: true } );

استراتژی‌های عملی بهینه‌سازی

بهینه‌سازی عملکرد بلاک‌ها یک فرآیند چندلایه است. در این بخش، یک چارچوب عملی برای بهینه‌سازی ارائه می‌شود که در پروژه‌های واقعی جواب داده است.

گام اول: اندازه‌گیری Baseline

قبل از هر تغییری، نمره Lighthouse و CWV را در یک صفحه نمونه ثبت کنید. اگر Baseline ندارید، نمی‌توانید بهبود را اندازه‌گیری کنید.

گام دوم: شناسایی بلاک‌های سنگین

با استفاده از DevTools Performance Panel، زمان JavaScript هر بلاک را اندازه‌گیری کنید. با استفاده از Query Monitor، کوئری‌های هر بلاک را تحلیل کنید. با استفاده از WebPageTest، حجم Assetهای هر بلاک را بررسی کنید.

گام سوم: حذف یا جایگزینی بلاک‌های مشکل‌دار

اگر بلاک شخص ثالثی Assetهای اضافی بارگذاری می‌کند، آن را با یک بلاک بومی یا یک بلاک سبک‌تر جایگزین کنید. اگر بلاک داینامیکی کوئری سنگین اجرا می‌کند، آن را با یک بلاک استاتیک یا با Cache جایگزین نمایید.

گام چهارم: بهینه‌سازی Assetها

از block.json برای ثبت بلاک استفاده کنید. Assetهای ضروری را در block.json تعریف کنید. از viewScript فقط برای بلاک‌های تعاملی استفاده کنید. از Interactivity API برای بلاک‌های نیمه‌تعاملی بهره ببرید.

گام پنجم: بهینه‌سازی CSS

در theme.json، فقط Presetهای ضروری را تعریف کنید. از useRootPaddingAwareAlignments استفاده کنید تا حجم CSS کاهش یابد. استایل‌های خاص بلاک‌ها را در styles.css تعریف کنید، نه در theme.json.

گام ششم: بهینه‌سازی تصاویر

از wp_get_attachment_image() برای تصاویر استفاده کنید تا srcset خودکار تولید شود. Lazy Loading را فعال کنید. تصاویر LCP را با fetchpriority="high" علامت‌گذاری کنید. تصاویر را به فرمت WebP یا AVIF تبدیل کنید.

گام هفتم: کش و Revalidation

Object Cache (Redis یا Memcached) را فعال کنید. برای بلاک‌های داینامیک، از Transient API استفاده کنید. برای Query Loopهای پرتکرار، نتیجه را در Transient ذخیره کنید. اگر با افزایش سرعت سایت وردپرسی آشنا شده باشید، این استراتژی‌ها برای شما آشناست.

گام هشتم: تست مجدد و تکرار

پس از هر تغییر، نمره Lighthouse و CWV را مجدداً اندازه‌گیری کنید. اگر بهبود حاصل نشد، گام دوم را تکرار کنید. بهینه‌سازی عملکرد یک فرآیند تکراری است، نه یک اقدام یک‌باره.

«بهینه‌سازی عملکرد یک پروژه نیست، یک فرآیند است. هر بلاک جدید، هر به‌روزرسانی افزونه، و هر تغییر طراحی می‌تواند تعادل عملکردی را بر هم بزند.»

پرسش‌های پرتکرار درباره عملکرد بلاک‌های وردپرس

آیا تعداد بلاک‌ها در یک صفحه بر سرعت سایت تأثیر می‌گذارد؟

تعداد بلاک‌ها به‌تنهایی معیار خوبی نیست. یک صفحه با ۵۰ بلاک سبک (مثل Heading، Paragraph، Image) می‌تواند سریع‌تر از صفحه‌ای با ۵ بلاک سنگین (مثل Slider، Map، Form) باشد. معیار مهم، مجموع Assetها، کوئری‌ها، و DOM Size است، نه تعداد بلاک‌ها.

چگونه بفهمم کدام بلاک سایت من را کند می‌کند؟

سه روش: اول، با Query Monitor کوئری‌های هر صفحه را بررسی کنید و ببینید کدام کوئری بالای ۱۰۰ میلی‌ثانیه است. دوم، با Chrome DevTools Performance Panel، زمان JavaScript هر بلاک را اندازه‌گیری کنید. سوم، با WebPageTest، Waterfall شبکه را تحلیل کنید و ببینید کدام فایل CSS یا JS حجم بالایی دارد.

آیا بلاک‌های داینامیک همیشه کندتر از بلاک‌های استاتیک هستند؟

خیر، همیشه این‌طور نیست. بلاک‌های داینامیک اگر با Cache مناسب پیاده‌سازی شوند، می‌توانند به‌سرعت بلاک‌های استاتیک باشند. تفاوت اصلی در این است که بلاک‌های استاتیک HTML در دیتابیس ذخیره می‌کنند و بلاک‌های داینامیک در هر درخواست تولید می‌کنند. اگر تولید سریع باشد (با Transient یا Object Cache)، تفاوت محسوس نیست.

آیا Interactivity API می‌تواند جایگزین کامل JavaScript در بلاک‌ها شود؟

خیر، Interactivity API برای بلاک‌های نیمه‌تعاملی (مثل Accordion، Tab، Toggle) طراحی شده، نه برای بلاک‌های پیچیده (مثل Slider، Map، Chart). برای بلاک‌های پیچیده، همچنان JavaScript کامل لازم است. اما برای بسیاری از موارد، Interactivity API می‌تواند جایگزین Hydration کامل شود.

چگونه theme.json را برای کاهش حجم CSS بهینه کنم؟

سه راه‌حل: اول، تعداد Presetها را کاهش دهید (رنگ، فونت، فاصله). دوم، از useRootPaddingAwareAlignments استفاده کنید. سوم، استایل‌های بلاک را در styles.css تعریف کنید، نه در theme.json. با این سه، می‌توانید حجم global-styles.css را تا ۵۰ درصد کاهش دهید.

آیا استفاده از بلاک‌های شخص ثالث بر سرعت سایت تأثیر می‌گذارد؟

بله، و به‌طور معمول بیشتر از بلاک‌های بومی. بلاک‌های شخص ثالث ممکن است Assetهای اضافی بارگذاری کنند، از کتابخانه‌های قدیمی استفاده کنند، یا با نسخه‌های جدید گوتنبرگ سازگار نباشند. قبل از نصب، تعداد نصب، آخرین به‌روزرسانی، و رفتار Asset را بررسی کنید.

چگونه تصاویر بلاک‌ها را برای LCP بهینه کنم؟

سه گام: اول، تصویر LCP را با fetchpriority="high" علامت‌گذاری کنید. دوم، تصویر را با <link rel="preload"> پیش‌بارگذاری کنید. سوم، تصویر را به فرمت WebP یا AVIF تبدیل کنید و ابعاد صریح (width و height) تعریف نمایید تا CLS کاهش یابد.

آیا Query Loop بر TTFB تأثیر می‌گذارد؟

بله، اگر Query Loop کوئری سنگین اجرا کند، TTFB افزایش می‌یابد. سه راه‌حل: اول، no_found_rows را در WP_Query فعال کنید. دوم، نتیجه را در Transient کش کنید. سوم، از queryId برای اشتراک نتایج بین چند بلاک استفاده کنید.

چگونه CSS یک بلاک را بهینه کنم؟

سه راه‌حل: اول، CSS را در block.json تعریف کنید تا Lazy Loading اعمال شود. دوم، از CSS Variables برای رنگ و فاصله استفاده کنید. سوم، از Logical Properties برای RTL و LTR پشتیبانی کنید. اگر با بهینه‌سازی CSS و کاهش حجم آن آشنا شده باشید، این رویکردها برای شما آشناست.

آیا استفاده از Polyfill در بلاک‌ها بر سرعت تأثیر می‌گذارد؟

بله، Polyfillها حجم JavaScript را افزایش می‌دهند و زمان Parse را بیشتر می‌کنند. در سال ۲۰۲۶، اکثر مرورگرهای مدرن از ویژگی‌های ES2020+ پشتیبانی می‌کنند و Polyfillها کمتر نیاز هستند. اگر Polyfill استفاده می‌کنید، فقط برای ویژگی‌های ضروری و با Conditional Loading باشد.

تحلیل مهندسی سطح پیشرفته

از منظر معماری نرم‌افزار، عملکرد بلاک‌های وردپرس یک مسئله Layered Optimization است که در سه لایه مستقل قابل تحلیل است: لایه ساخت (Build)، لایه رندر (Render)، و لایه انتقال (Transfer). هر لایه، نقاط ضعف و راه‌حل‌های متفاوتی دارد و نادیده گرفتن هرکدام، به گلوگاه در لایه دیگر منجر می‌شود.

در لایه ساخت، چالش اصلی Tree Shaking و Code Splitting است. اگر بلاک شما از یک کتابخانه بزرگ استفاده می‌کند اما فقط بخش کوچکی از آن را نیاز دارد، بدون Tree Shaking کل کتابخانه در Bundle نهایی قرار می‌گیرد. ابزار @wordpress/scripts به‌طور پیش‌فرض این ویژگی را فعال می‌کند، اما اگر پیکربندی سفارشی داشته باشید، ممکن است غیرفعال شود. راه‌حل پیشرفته، استفاده از Dynamic Import برای بارگذاری بخش‌های سنگین بلاک به‌صورت Lazy است:

const HeavyComponent = lazy( () => import( './HeavyComponent' ) );

در لایه رندر، چالش اصلی Hydration Cost است. هر بلاک تعاملی، یک هزینه Hydration دارد که به پیچیدگی Component، تعداد State، و تعداد Event Listener بستگی دارد. راه‌حل پیشرفته، استفاده از Partial Hydration با Interactivity API است که فقط بخش‌های تعاملی را Hydrate می‌کند. این رویکرد، مشابه Islands Architecture در Astro و Qwik است و در بلاک‌های گوتنبرگ از نسخه ۶.۵ پشتیبانی می‌شود. چالش دیگر، DOM Size است: اگر بلاک شما DOM زیادی تولید کند، مرورگر برای Layout و Paint زمان بیشتری مصرف می‌کند. راه‌حل، استفاده از CSS Containment است که به مرورگر می‌گوید یک بخش از DOM مستقل است و می‌تواند جداگانه رندر شود:

.my-block {
    contain: layout style paint;
}

در لایه انتقال، چالش اصلی Cache Invalidation است. بلاک‌های داینامیک، نتایج خود را در Cache نگه می‌دارند اما وقتی محتوا تغییر می‌کند، باید Cache را باطل کنند. راه‌حل پیشرفته، استفاده از Cache Tags یا Cache Keys مبتنی بر Version است که به‌جای باطل کردن کل Cache، فقط بخش مرتبط را باطل می‌کند. اگر با بهینه‌سازی پیشرفته دیتابیس وردپرس آشنا شده باشید، این رویکرد را به‌عنوان بخشی از یک استراتژی جامع Cache می‌شناسید.

چالش بنیادین‌تر، Observability است: در یک سایت با ده‌ها بلاک و صدها صفحه، چگونه می‌توان فهمید کدام بلاک در کدام صفحه کندی ایجاد می‌کند؟ راه‌حل استاندارد، استفاده از Real User Monitoring (RUM) است که CWV را از مرورگر کاربران واقعی جمع‌آوری می‌کند و به تفکیک صفحه و بلاک گزارش می‌دهد. ابزارهایی مثل پروفایلینگ وردپرس با New Relic یا Google Analytics 4 می‌توانند این سطح از Observability را فراهم کنند. ترکیب RUM با Synthetic Monitoring (مثل Lighthouse CI)، یک تصویر کامل از عملکرد سایت در اختیار تیم قرار می‌دهد.

در نهایت، عملکرد بلاک‌های وردپرس یک Quality Attribute است که مانند امنیت و دسترس‌پذیری، باید در تمام مراحل توسعه لحاظ شود. تیم‌هایی که عملکرد را به‌عنوان یک الزام اولیه در نظر می‌گیرند، محصولاتی می‌سازند که در مقیاس میلیونی نیز پایدار می‌مانند. تیم‌هایی که عملکرد را به مرحله بعد از انتشار موکول می‌کنند، با بدهی فنی سنگینی مواجه می‌شوند که رفع آن چند برابر هزینه دارد.

اگر این تجربه را در یک پروژه واقعی داشته‌اید، جالب است بدانید کدام بلاک بیشترین کندی را ایجاد کرد. تجربه خودتان را در دیدگاه‌ها بنویسید؛ به‌خصوص اگر راه‌حل دیگری پیدا کرده‌اید که می‌تواند برای خواننده بعدی مفید باشد. ⚡

همچنین اگر می‌خواهید در مورد پیاده‌سازی عملی بلاک‌ها و بهینه‌سازی آن‌ها بیشتر بدانید، ساخت بلاک سفارشی گوتنبرگ از صفر و بهینه‌سازی سرعت سایت و اهمیت آن می‌توانند نقاط شروع خوبی باشند.