INP Optimization در وردپرس یعنی کاهش زمان پاسخ صفحه به تعامل کاربر، از لحظه کلیک یا لمس تا لحظه‌ای که مرورگر بازخورد بصری نمایش می‌دهد.

INP (Interaction to Next Paint) جایگزین FID (First Input Delay) شده و برخلاف آن، کل طول عمر صفحه را پوشش می‌دهد، نه فقط اولین تعامل.

سه جزء اصلی INP وجود دارد: تأخیر ورودی، زمان پردازش رویداد و زمان ارائه بازخورد بصری.

سایت‌هایی که تنها روی LCP تمرکز کرده‌اند، اغلب در INP شکست می‌خورند، چون این معیار ماهیت متفاوتی دارد.

هدف این نوشته، روشن‌کردن تفاوت‌های بنیادین INP با معیارهای دیگر و ارائه الگوهای عملی برای بهبود آن در سایت‌های وردپرسی است.

در پروژه‌ای که همه معیارهایش سبز بود، یک شکایت تکرارشونده وجود داشت: کاربران می‌گفتند دکمه افزودن به سبد خرید با تأخیر پاسخ می‌دهد. بررسی که انجام شد، ریشه در یک اسکریپت تحلیلی بود که در زمان کلیک، کل رشته اصلی مرورگر را برای چند صد میلی‌ثانیه بلاک می‌کرد. رفع همان یک مورد، تجربه کاربر را به‌طور محسوس بهبود داد. این تجربه نشان داد که INP معیاری است که در جزئیات پنهان می‌شود، نه در کلیات.

INP دقیقاً چه چیزی را اندازه می‌گیرد

INP (Interaction to Next Paint) معیاری است که فاصله زمانی میان یک تعامل کاربر و لحظه‌ای که مرورگر بازخورد بصری مربوطه را نمایش می‌دهد را اندازه می‌گیرد. تعامل می‌تواند کلیک، لمس، فشار کلید یا هر ورودی دیگری باشد که کاربر انجام می‌دهد.

تفاوت اصلی INP با معیارهای دیگر Core Web Vitals در ماهیت آن است. LCP (Largest Contentful Paint) زمان بارگذاری عنصر اصلی صفحه را می‌سنجد و CLS (Cumulative Layout Shift) مقدار جهش چیدمان را. اما INP به تعامل کاربر مربوط است، یعنی پس از آنکه صفحه بارگذاری شد و کاربر شروع به استفاده از آن کرد.

این تفاوت، INP را به معیاری متمایز تبدیل می‌کند. یک سایت می‌تواند LCP عالی و CLS صفر داشته باشد اما INP ضعیفی داشته باشد. علت این است که LCP و CLS در زمان بارگذاری اندازه‌گیری می‌شوند اما INP در طول عمر صفحه.

مقدار مطلوب INP کمتر از ۲۰۰ میلی‌ثانیه است. مقدار ۲۰۰ تا ۵۰۰ میلی‌ثانیه نیازمند بهبود است و مقدار بیش از ۵۰۰ میلی‌ثانیه ضعیف در نظر گرفته می‌شود.

نکته مهم این است که INP یک معیار تجمعی است، نه یک اندازه‌گیری لحظه‌ای. مرورگر همه تعامل‌های کاربر در طول عمر صفحه را ثبت می‌کند و در نهایت، یکی از آن‌ها (معمولاً بدترین یا نزدیک به بدترین) را به‌عنوان INP گزارش می‌دهد.

این رفتار، INP را به معیاری سخت‌گیرانه تبدیل می‌کند. حتی اگر ۹۹ تعامل سریع باشند و یک تعامل کند، همان یک تعامل می‌تواند INP را تحت تأثیر قرار دهد. اصول کلی این معیار در چارچوب Core Web Vitals چیست و INP و تأثیر آن بر تجربه کاربر آمده است.

INP تنها معیاری است که رفتار سایت را در لحظه استفاده واقعی می‌سنجد، نه در لحظه بارگذاری اولیه.

چرا INP جای FID را گرفت

FID (First Input Delay) معیار قبلی برای اندازه‌گیری تعامل کاربر بود. این معیار تنها اولین تعامل کاربر را اندازه می‌گرفت و تنها تأخیر ورودی را می‌سنجید، نه زمان پردازش یا زمان ارائه بازخورد.

محدودیت‌های FID در عمل آشکار شد. یک سایت می‌توانست FID عالی داشته باشد اما تجربه کاربری ضعیفی در تعامل‌های بعدی. مثلاً اولین کلیک سریع پاسخ می‌گرفت، اما کلیک بعدی روی دکمه افزودن به سبد خرید چند ثانیه طول می‌کشید.

INP این محدودیت‌ها را برطرف می‌کند. این معیار همه تعامل‌ها را پوشش می‌دهد، نه فقط اولین. همچنین سه جزء تأخیر ورودی، زمان پردازش و زمان ارائه را اندازه می‌گیرد، نه فقط تأخیر ورودی.

مسئله مهم دیگر، تکرار تعامل است. اگر کاربر چند بار روی یک دکمه کلیک کند، همه این کلیک‌ها در INP لحاظ می‌شوند. این رفتار، تفاوت مهمی با FID دارد که تنها اولین تعامل را می‌سنجید.

تغییر از FID به INP در سال‌های اخیر انجام شد و سایت‌هایی که تنها بر FID تمرکز کرده بودند، نیازمند بازبینی رویکرد خود شدند. اصول تفصیلی این تغییر در چرا FID جای خود را به INP داد آمده است.

نکته مهم دیگر، تفاوت در واحد اندازه‌گیری است. FID تنها میلی‌ثانیه را می‌سنجید، اما INP می‌تواند به چند ثانیه برسد اگر پردازش رویداد طولانی باشد. این تفاوت، INP را به معیاری سخت‌گیرانه‌تر تبدیل می‌کند.

سه جزء سازنده INP

INP از سه جزء تشکیل شده که هر یک می‌تواند منبع تأخیر باشد. شناخت این سه جزء، پیش‌نیاز هر بهینه‌سازی است.

جزء اول، تأخیر ورودی (Input Delay) است. این جزء، فاصله زمانی میان لحظه تعامل کاربر و لحظه‌ای که مرورگر شروع به پردازش رویداد می‌کند را می‌سنجد. اگر رشته اصلی مرورگر مشغول اجرای کد دیگری باشد، این تأخیر افزایش می‌یابد.

جزء دوم، زمان پردازش (Processing Time) است. این جزء، زمان اجرای همه callbackهای مربوط به رویداد را اندازه می‌گیرد. اگر کد رویداد طولانی باشد، این زمان افزایش می‌یابد.

جزء سوم، تأخیر ارائه (Presentation Delay) است. این جزء، فاصله زمانی میان پایان پردازش رویداد و لحظه‌ای که مرورگر بازخورد بصری را نمایش می‌دهد را می‌سنجد. اگر مرورگر نیاز به رندر مجدد پیچیده داشته باشد، این تأخیر افزایش می‌یابد.

INP = Input Delay + Processing Time + Presentation Delay

این فرمول، تصویر روشنی از INP ارائه می‌دهد. هر بهینه‌سازی باید یکی از این سه جزء را هدف بگیرد.

جزءمنبع اصلی تأخیرراه‌حل اصلی
Input Delayبلاک بودن رشته اصلیشکستن تسک‌های طولانی
Processing Timeکد رویداد طولانیبهینه‌سازی منطق رویداد
Presentation Delayرندر مجدد پیچیدهکاهش پیچیدگی DOM

در عمل، بیشتر سایت‌های وردپرسی با مشکل در جزء اول (تأخیر ورودی) و دوم (زمان پردازش) مواجه هستند. تأخیر ارائه در سایت‌هایی که DOM بسیار پیچیده دارند، مسئله‌ساز می‌شود.

مسئله مهم دیگر، نسبت این سه جزء در تعامل‌های مختلف است. یک کلیک ساده ممکن است تأخیر ورودی بالایی داشته باشد اما زمان پردازش کوتاهی. یک ارسال فرم ممکن است تأخیر ورودی کوتاه اما زمان پردازش طولانی داشته باشد. تحلیل هر تعامل بر اساس ترکیب این سه جزء انجام می‌شود.

تأخیر ورودی و منابع آن

تأخیر ورودی، اولین جزء INP است و در بسیاری از سایت‌ها، بزرگ‌ترین سهم را دارد. این تأخیر، نتیجه مستقیم بلاک بودن رشته اصلی مرورگر است.

رشته اصلی مرورگر، مسئولیت‌های متعددی دارد. اجرای جاوااسکریپت، محاسبه استایل‌ها، ساخت چیدمان و رندر نهایی، همه در این رشته انجام می‌شوند. اگر یکی از این عملیات طولانی باشد، تعامل کاربر منتظر می‌ماند.

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

// Example: Long task blocking main thread
function processLargeData() {
    const data = [];
    for (let i = 0; i < 1000000; i++) {
        data.push({ id: i, value: Math.random() });
    }
    return data;
}

// This blocks the main thread for hundreds of milliseconds
document.querySelector("#btn").addEventListener("click", processLargeData);

در این نمونه، پردازش داده حجیم در زمان کلیک، رشته اصلی را برای صدها میلی‌ثانیه بلاک می‌کند. اگر کاربر در این بازه روی دکمه دیگری کلیک کند، تعامل او منتظر می‌ماند.

راه‌حل‌های کاهش تأخیر ورودی در سه سطح قابل بررسی است. سطح اول، شکستن تسک‌های طولانی به قطعات کوچک‌تر با استفاده از setTimeout یا requestIdleCallback است.

async function processDataInChunks(items) {
    const CHUNK_SIZE = 1000;
    for (let i = 0; i < items.length; i += CHUNK_SIZE) {
        processChunk(items.slice(i, i + CHUNK_SIZE));
        await new Promise(resolve => setTimeout(resolve, 0));
    }
}

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

سطح دوم، انتقال پردازش به Web Worker است. Web Worker یک رشته جداگانه است که می‌تواند پردازش‌های سنگین را بدون بلاک کردن رشته اصلی انجام دهد.

const worker = new Worker("/wp-content/themes/mytheme/js/processor.js");

document.querySelector("#btn").addEventListener("click", () => {
    worker.postMessage({ action: "process", data: largeData });
});

worker.addEventListener("message", (e) => {
    displayResult(e.data);
});

Web Worker برای پردازش داده، رمزنگاری و محاسبات پیچیده مناسب است. اما برای عملیات DOM استفاده نمی‌شود، چون DOM تنها در رشته اصلی قابل دسترسی است.

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

زمان پردازش رویداد و گلوگاه‌های آن

زمان پردازش، دومین جزء INP است. این جزء، زمان اجرای همه callbackهای مربوط به رویداد را اندازه می‌گیرد و در سایت‌های وردپرسی می‌تواند به چند صد میلی‌ثانیه برسد.

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

سه گلوگاه اصلی در پردازش رویداد در وردپرس وجود دارد. گلوگاه اول، اجرای query های سنگین در زمان رویداد است. گلوگاه دوم، به‌روزرسانی حجیم DOM است. گلوگاه سوم، محاسبات پیچیده در زمان رویداد است.

// Slow: heavy processing in event handler
document.querySelector(".add-to-cart").addEventListener("click", function() {
    // Recalculate prices for all products
    const allProducts = document.querySelectorAll(".product");
    allProducts.forEach(product => {
        recalculateProductPrice(product);
    });
    // Update cart total
    updateCartTotal();
});

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

راه‌حل، محدودسازی پردازش به محصول تغییر‌یافته است.

document.querySelector(".add-to-cart").addEventListener("click", function() {
    // Only recalculate the changed product
    recalculateProductPrice(this.closest(".product"));
    // Update cart total
    updateCartTotal();
});

این تغییر کوچک، زمان پردازش را به‌طور محسوس کاهش می‌دهد.

مسئله مهم دیگر، به‌روزرسانی DOM است. هر تغییر در DOM، باعث محاسبه مجدد استایل‌ها و چیدمان می‌شود. اگر این تغییرات متعدد باشند، زمان پردازش افزایش می‌یابد.

راه‌حل استاندارد، تجمیع تغییرات DOM است. به‌جای اعمال چندین تغییر جداگانه، همه تغییرات در یک عملیات اعمال می‌شوند.

// Instead of multiple DOM updates
function updateUI(data) {
    // Batch DOM changes using DocumentFragment
    const fragment = document.createDocumentFragment();
    data.forEach(item => {
        const element = document.createElement("div");
        element.textContent = item.label;
        fragment.appendChild(element);
    });
    document.querySelector("#container").appendChild(fragment);
}

این الگو، تعداد دفعات محاسبه مجدد استایل‌ها و چیدمان را کاهش می‌دهد.

تأخیر ارائه و رندر مجدد

تأخیر ارائه، سومین جزء INP است. این جزء، فاصله زمانی میان پایان پردازش رویداد و لحظه‌ای که مرورگر بازخورد بصری را نمایش می‌دهد را می‌سنجد.

پس از اجرای callback رویداد، مرورگر باید تغییرات DOM را اعمال کند، استایل‌ها را محاسبه کند، چیدمان را بازسازی کند و نهایتاً رندر را انجام دهد. هر یک از این مراحل زمان می‌برد و مجموع آن‌ها، تأخیر ارائه را می‌سازد.

سه عامل اصلی بر تأخیر ارائه اثر می‌گذارند. عامل اول، پیچیدگی DOM است. هر چه تعداد عناصر DOM بیشتر باشد، محاسبه چیدمان طولانی‌تر است. عامل دوم، پیچیدگی استایل‌ها است. هر چه تعداد قواعد CSS بیشتر باشد، محاسبه استایل طولانی‌تر است. عامل سوم، حجم رندر مجدد است.

// Slow: triggers full layout recalculation
document.querySelector("#container").style.width = "500px";

// Then reading layout properties forces synchronous layout
const height = document.querySelector("#container").offsetHeight;

// Then another write
document.querySelector("#container").style.height = "300px";

این الگو که با نام Layout Thrashing شناخته می‌شود، باعث می‌شود مرورگر چندین بار چیدمان را محاسبه کند.

راه‌حل، جداسازی خواندن و نوشتن است. همه عملیات خواندن ابتدا و همه عملیات نوشتن پس از آن انجام می‌شوند.

// Fast: batch reads and writes separately
// Phase 1: Read
const height = document.querySelector("#container").offsetHeight;
const width = document.querySelector("#container").offsetWidth;

// Phase 2: Write
document.querySelector("#container").style.width = "500px";
document.querySelector("#container").style.height = "300px";

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

مسئله مهم دیگر، انتخاب خصوصیات CSS است. برخی خصوصیات مانند transform و opacity تنها بر لایه کامپوزیت اثر می‌گذارند و نیازمند محاسبه مجدد چیدمان نیستند. برخی دیگر مانند width، height و margin باعث محاسبه مجدد چیدمان می‌شوند.

/* Fast: only compositing */
.animate {
    transform: translateX(100px);
    opacity: 0.5;
}

/* Slow: triggers layout recalculation */
.animate {
    left: 100px;
    width: 200px;
}

استفاده از خصوصیات مناسب، تأخیر ارائه را به‌طور محسوس کاهش می‌دهد.

رشته اصلی و مسئله بلاک شدن

رشته اصلی مرورگر، تنها رشته‌ای است که می‌تواند کد جاوااسکریپت را اجرا کند و DOM را دستکاری نماید. اگر این رشته مشغول اجرای کد طولانی باشد، تعامل کاربر منتظر می‌ماند.

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

ابزار Chrome DevTools در تب Performance، امکان مشاهده دقیق رفتار رشته اصلی را فراهم می‌کند. در این تب، می‌توان تسک‌های طولانی را شناسایی کرد.

// Use Performance API to detect long tasks
const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
        if (entry.duration > 50) {
            console.log("Long task detected:", entry.duration, "ms");
            console.log("Attribution:", entry.attribution);
        }
    }
});
observer.observe({ entryTypes: ["longtask"] });

این کد، تسک‌های طولانی بیش از ۵۰ میلی‌ثانیه را در کنسول مرورگر گزارش می‌دهد. تحلیل این گزارش، نقاط بلاک شدن را مشخص می‌کند.

راه‌حل‌های کاهش بلاک شدن در سه سطح قابل بررسی است. سطح اول، کاهش حجم جاوااسکریپت است. حذف کتابخانه‌های غیرضروری و استفاده از نسخه‌های سبک‌تر می‌تواند زمان اجرا را کاهش دهد.

سطح دوم، انتقال پردازش به Web Worker است. برای پردازش‌های سنگین که نیازی به DOM ندارند، Web Worker گزینه مناسبی است.

سطح سوم، استفاده از الگوهای زمان‌بندی است. requestIdleCallback اجازه می‌دهد پردازش‌های کم‌اهمیت در زمان‌های بیکاری مرورگر اجرا شوند.

function processLowPriorityData(data) {
    if ("requestIdleCallback" in window) {
        requestIdleCallback(() => {
            data.forEach(processItem);
        });
    } else {
        setTimeout(() => data.forEach(processItem), 1);
    }
}

این الگو، پردازش‌های کم‌اهمیت را به زمان‌های بیکاری منتقل می‌کند و رشته اصلی را برای تعامل کاربر آزاد می‌گذارد.

مسائل مرتبط با بهینه‌سازی جاوااسکریپت در چارچوب بهینه‌سازی جاوااسکریپت به‌تفصیل آمده است.

Long Tasks و اثر آن‌ها بر INP

Long Task به تسکی گفته می‌شود که بیش از ۵۰ میلی‌ثانیه طول می‌کشد. این تسک‌ها، منبع اصلی تأخیر ورودی در INP هستند.

مرورگر رشته اصلی را در اختیار یک تسک قرار می‌دهد تا زمانی که آن تسک پایان یابد. اگر تسکی ۵۰۰ میلی‌ثانیه طول بکشد، هر تعامل کاربر در این بازه، حداقل ۵۰۰ میلی‌ثانیه منتظر می‌ماند.

منابع اصلی Long Task در وردپرس شامل چند دسته است. دسته اول، بارگذاری و اجرای اسکریپت‌های بزرگ. دسته دوم، پردازش‌های سنگین در زمان رویداد. دسته سوم، محاسبات حجیم در زمان راه‌اندازی صفحه.

// Example of a long task pattern
function heavyComputation() {
    // This blocks the main thread for hundreds of milliseconds
    const result = [];
    for (let i = 0; i < 100000; i++) {
        result.push(complexCalculation(i));
    }
    return result;
}

راه‌حل، شکستن Long Task به تسک‌های کوچک‌تر است. مرورگر در بین تسک‌ها، فرصت پردازش تعامل کاربر را دارد.

async function heavyComputationInChunks() {
    const result = [];
    const CHUNK_SIZE = 1000;
    
    for (let i = 0; i < 100000; i += CHUNK_SIZE) {
        const chunk = [];
        for (let j = 0; j < CHUNK_SIZE; j++) {
            chunk.push(complexCalculation(i + j));
        }
        result.push(...chunk);
        
        // Yield to main thread
        await new Promise(resolve => setTimeout(resolve, 0));
    }
    
    return result;
}

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

مسئله مهم دیگر، شناسایی Long Taskها در پروژه واقعی است. ابزار PerformanceObserver و تب Performance در Chrome DevTools، این تسک‌ها را نمایش می‌دهند.

در سایت‌های وردپرسی، معمولاً بارگذاری اسکریپت‌های متعدد در زمان راه‌اندازی، منبع اصلی Long Task است. کاهش تعداد این اسکریپت‌ها و بارگذاری آن‌ها به‌صورت تأخیری، بخشی از راه‌حل است. اصول تفصیلی در مهم‌ترین توابع وردپرس برای توسعه‌دهندگان از منظر بارگذاری منابع آمده است.

هر تسکی که بیش از ۵۰ میلی‌ثانیه طول بکشد، یک تعامل کاربر را به تأخیر می‌اندازد؛ این تسک‌ها، دشمن پنهان INP هستند.

جاوااسکریپت و مدیریت بار آن

جاوااسکریپت بزرگ‌ترین منبع تأخیر در رشته اصلی مرورگر است. هر کیلوبایت اضافه، زمان تجزیه، کامپایل و اجرا را افزایش می‌دهد.

سه راهبرد اصلی برای مدیریت بار جاوااسکریپت در وردپرس وجود دارد. راهبرد اول، کاهش حجم با حذف کدهای غیرضروری است. راهبرد دوم، انتقال بارگذاری به زمان‌های مناسب است. راهبرد سوم، اجرای کد در محیط‌های مستقل است.

کاهش حجم، با چند تکنیک انجام می‌شود. حذف کتابخانه‌های غیرضروری. استفاده از نسخه‌های سبک‌تر. حذف پلی‌فیل‌های غیرضروری. این کارها در زمان توسعه، نه در زمان اجرا، انجام می‌شوند.

انتقال بارگذاری، با استفاده از صفت‌های async و defer انجام می‌شود. این صفت‌ها، زمان بارگذاری اسکریپت را از زمان تجزیه HTML جدا می‌کنند.

<script src="analytics.js" async></script>
<script src="main.js" defer></script>

صفت async باعث می‌شود اسکریپت به‌طور موازی بارگذاری و در اولین فرصت اجرا شود. صفت defer باعث می‌شود اسکریپت به‌طور موازی بارگذاری اما پس از تجزیه HTML اجرا شود.

در وردپرس، افزودن این صفت‌ها با استفاده از فیلترهای مناسب انجام می‌شود.

add_filter( "script_loader_tag", function ( $tag, $handle ) {
    $async_scripts = array( "analytics", "ads" );
    $defer_scripts = array( "main-script", "carousel" );
    
    if ( in_array( $handle, $async_scripts, true ) ) {
        return str_replace( " src=", " async src=", $tag );
    }
    if ( in_array( $handle, $defer_scripts, true ) ) {
        return str_replace( " src=", " defer src=", $tag );
    }
    return $tag;
}, 10, 2 );

این الگو، صفت‌های مناسب را به اسکریپت‌های مشخص اضافه می‌کند.

راهبرد سوم، انتقال کد به Web Worker است. برای پردازش‌های سنگین که نیازی به DOM ندارند، این راهبرد مؤثر است.

// main.js
const worker = new Worker("/wp-content/themes/mytheme/js/worker.js");

worker.addEventListener("message", (e) => {
    document.querySelector("#result").textContent = e.data;
});

document.querySelector("#btn").addEventListener("click", () => {
    worker.postMessage({ action: "compute", data: inputData });
});

// worker.js
self.addEventListener("message", (e) => {
    const result = performHeavyComputation(e.data);
    self.postMessage(result);
});

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

مسائل تفصیلی این حوزه در بهینه‌سازی جاوااسکریپت آمده است.

اسکریپت‌های شخص ثالث

اسکریپت‌های شخص ثالث یکی از پرتکرارترین منابع کاهش INP در سایت‌های وردپرسی هستند. این اسکریپت‌ها خارج از کنترل توسعه‌دهنده سایت اجرا می‌شوند و می‌توانند رشته اصلی را برای مدت طولانی بلاک کنند.

دسته‌های اصلی اسکریپت‌های شخص ثالث شامل چند گروه است. ابزارهای تحلیلی مانند Google Analytics. ابزارهای تبلیغات. ابزارهای چت آنلاین. ابزارهای نظرسنجی و بازخورد. ابزارهای شبکه‌های اجتماعی. افزونه‌های شخص ثالث وردپرس.

سه راهبرد برای کاهش اثر این اسکریپت‌ها وجود دارد. راهبرد اول، حذف کامل اسکریپت‌های غیرضروری است. راهبرد دوم، بارگذاری تأخیری است. راهبرد سوم، ایزوله‌سازی است.

حذف کامل، ساده‌ترین راهبرد است اما نیازمند بازبینی دقیق نیازها است. اگر ابزار تحلیلی استفاده نمی‌شود، حذف آن بهترین راه‌حل است.

بارگذاری تأخیری، با استفاده از صفت async یا defer یا با بارگذاری در زمان تعامل کاربر انجام می‌شود.

// Load third-party script on user interaction
function loadOnInteraction(scriptUrl) {
    const events = ["mousedown", "touchstart", "keydown", "scroll"];
    
    const loadScript = () => {
        const script = document.createElement("script");
        script.src = scriptUrl;
        script.async = true;
        document.head.appendChild(script);
        events.forEach(event => {
            document.removeEventListener(event, loadScript);
        });
    };
    
    events.forEach(event => {
        document.addEventListener(event, loadScript, { once: true, passive: true });
    });
}

loadOnInteraction("https://example.com/analytics.js");

این الگو، اسکریپت را تنها در زمان اولین تعامل کاربر بارگذاری می‌کند.

ایزوله‌سازی، با استفاده از iframe یا Web Worker انجام می‌شود. این رویکرد، اسکریپت را از رشته اصلی جدا می‌کند.

<iframe src="https://example.com/widget" loading="lazy" sandbox="allow-scripts"></iframe>

استفاده از loading="lazy" روی iframe، بارگذاری آن را تا زمان نزدیک شدن کاربر به آن به تأخیر می‌اندازد.

مسائل مرتبط با اسکریپت‌های شخص ثالث و اثر آن‌ها بر امنیت در هدرهای امنیتی HTTP آمده است. رعایت این اصول، هم امنیت و هم عملکرد را بهبود می‌دهد.

عوامل اختصاصی وردپرس مؤثر بر INP

چند عامل اختصاصی وردپرس بر INP اثر می‌گذارند که شناخت آن‌ها برای بهینه‌سازی ضروری است.

عامل اول، افزونه‌های متعدد است. هر افزونه ممکن است فایل جاوااسکریپت خود را بارگذاری کند. با افزایش تعداد افزونه‌ها، تعداد فایل‌های بارگذاری‌شده افزایش می‌یابد.

عامل دوم، قالب‌های سنگین است. برخی قالب‌ها کتابخانه‌های جاوااسکریپت حجیم را بارگذاری می‌کنند که برای عملکرد پایه سایت ضروری نیستند.

عامل سوم، برگه‌سازها هستند. برگه‌سازهایی مانند Elementor، Divi و WPBakery معمولاً جاوااسکریپت زیادی تولید می‌کنند که بار رشته اصلی را افزایش می‌دهد.

// Identify jQuery dependency and consider removing it
add_action( "wp_enqueue_scripts", function () {
    // Only dequeue jQuery on pages where it is not needed
    if ( ! is_admin() && ! is_singular( "product" ) ) {
        // Careful: some plugins depend on jQuery
        // wp_dequeue_script( "jquery" );
    }
} );

عامل چهارم، پلاگین‌های کش است. برخی افزونه‌های کش، جاوااسکریپت اضافه‌ای تزریق می‌کنند که ممکن است بر INP اثر بگذارد.

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

// Conditionally load scripts only where needed
add_action( "wp_enqueue_scripts", function () {
    if ( is_front_page() ) {
        wp_enqueue_script( "homepage-specific", get_template_directory_uri() . "/js/home.js", array(), "1.0", true );
    }
    if ( is_product() ) {
        wp_enqueue_script( "product-specific", get_template_directory_uri() . "/js/product.js", array(), "1.0", true );
    }
} );

این الگو، اسکریپت‌ها را تنها در صفحاتی که به آن‌ها نیاز دارند بارگذاری می‌کند.

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

اندازه‌گیری دقیق INP

اندازه‌گیری دقیق INP، پیش‌نیاز هر بهینه‌سازی است. سه سطح اندازه‌گیری وجود دارد: سطح آزمایشگاهی، سطح میدانی و سطح محلی.

در سطح آزمایشگاهی، ابزارهایی مانند Lighthouse و PageSpeed Insights عدد INP را تخمین می‌زنند. این ابزارها از شرایط کنترل‌شده استفاده می‌کنند و تصویر کلی ارائه می‌دهند.

در سطح میدانی، داده‌های واقعی کاربران از CrUX (Chrome User Experience Report) استفاده می‌شود. این داده‌ها بر اساس تعامل‌های واقعی کاربران ساخته می‌شوند و تصویر واقع‌بینانه‌تری ارائه می‌دهند.

در سطح محلی، می‌توان با استفاده از APIهای مرورگر، INP را در پروژه واقعی اندازه گرفت.

// Measure INP locally using web-vitals library
import { onINP } from "web-vitals";

onINP((metric) => {
    console.log("INP:", metric.value, "ms");
    console.log("Attribution:", metric.attribution);
    
    // Send to analytics
    if (navigator.sendBeacon) {
        navigator.sendBeacon("/analytics", JSON.stringify({
            name: metric.name,
            value: metric.value,
            attribution: metric.attribution
        }));
    }
});

این الگو، از کتابخانه web-vitals برای اندازه‌گیری INP در زمان واقعی استفاده می‌کند و اطلاعات را به سرور تحلیل ارسال می‌نماید.

ابزار دیگر، تب Performance در Chrome DevTools است. این ابزار، تفکیک دقیق مراحل را نشان می‌دهد و امکان شناسایی تسک‌های طولانی را فراهم می‌کند.

ابزار سوم، PerformanceObserver برای شناسایی تسک‌های طولانی است.

const observer = new PerformanceObserver((list) => {
    list.getEntries().forEach(entry => {
        if (entry.duration > 50) {
            console.log("Long task:", {
                duration: entry.duration,
                startTime: entry.startTime,
                attribution: entry.attribution
            });
        }
    });
});
observer.observe({ entryTypes: ["longtask"] });

تحلیل این داده‌ها، نقاط بلاک شدن را مشخص می‌کند.

مسائل مرتبط با اندازه‌گیری در چارچوب ابزارهای سنجش Core Web Vitals به‌تفصیل آمده است.

الگوهای عملی بهینه‌سازی

بهینه‌سازی INP نیازمند ترکیبی از الگوهای شناخته‌شده است. هر الگو، یکی از سه جزء INP را هدف می‌گیرد.

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

async function processInChunks(items, chunkSize = 100) {
    for (let i = 0; i < items.length; i += chunkSize) {
        const chunk = items.slice(i, i + chunkSize);
        processChunk(chunk);
        await yieldToMainThread();
    }
}

function yieldToMainThread() {
    return new Promise(resolve => setTimeout(resolve, 0));
}

الگوی دوم، کاهش حجم جاوااسکریپت است. حذف کتابخانه‌های غیرضروری، استفاده از نسخه‌های سبک‌تر و بارگذاری تأخیری.

الگوی سوم، بهینه‌سازی DOM است. کاهش تعداد عناصر، تجمیع تغییرات و پرهیز از Layout Thrashing.

// Batch DOM updates
const updates = [];
function queueUpdate(element, property, value) {
    updates.push({ element, property, value });
}

function flushUpdates() {
    updates.forEach(({ element, property, value }) => {
        element.style[property] = value;
    });
    updates.length = 0;
}

// Later, flush all updates at once
flushUpdates();

الگوی چهارم، استفاده از Passive Listeners است. این تنظیم، برای رویدادهای اسکرول و لمس مناسب است و از بلاک شدن رشته اصلی جلوگیری می‌کند.

document.querySelector("#container").addEventListener("scroll", handler, {
    passive: true
});

الگوی پنجم، انتقال پردازش به Web Worker است. برای پردازش‌های سنگین که نیازی به DOM ندارند.

الگوی ششم، استفاده از CSS به‌جای جاوااسکریپت برای انیمیشن‌ها است. انیمیشن‌های CSS در لایه کامپوزیت اجرا می‌شوند و نیازی به رشته اصلی ندارند.

.fade-in {
    opacity: 0;
    transition: opacity 0.3s ease;
}

.fade-in.visible {
    opacity: 1;
}

این الگو، رشته اصلی را برای تعامل کاربر آزاد می‌گذارد.

جدول تصمیم‌گیری بهینه‌سازی

مسئلهالگوی تشخیصراه‌حل پیشنهادی
تأخیر ورودی بالاLong Tasks در Performance tabشکستن تسک‌ها، Web Worker
زمان پردازش طولانیcallback length بالابهینه‌سازی منطق رویداد
تأخیر ارائه بالاLayout Thrashingتجمیع تغییرات DOM
اسکریپت شخص ثالث سنگینThird-party in waterfallبارگذاری تأخیری، ایزوله‌سازی
حجم جاوااسکریپت بالاBundle size آنالیزکاهش حجم، حذف غیرضروری
انیمیشن‌های جاوااسکریپتیCPU usage در تب Performanceانتقال به CSS
حجم DOM بالاDOM node countکاهش عناصر، Virtual DOM
اسکریپت‌های متعدد وردپرسیNetwork waterfallشرطی‌سازی بارگذاری

اشتباهات رایج

نخستین اشتباه، تمرکز بر LCP و نادیده گرفتن INP است. اگرچه این دو معیار مرتبط هستند، اما عوامل مؤثر بر آن‌ها متفاوت است. سایت‌هایی که تنها بر LCP تمرکز می‌کنند، اغلب در INP ضعیف عمل می‌کنند.

دومین اشتباه، بی‌توجهی به اسکریپت‌های شخص ثالث است. این اسکریپت‌ها خارج از کنترل توسعه‌دهنده هستند اما اثر بزرگی بر INP دارند.

سومین اشتباه، نادیده گرفتن تسک‌های طولانی است. هر تسک بیش از ۵۰ میلی‌ثانیه، یک تعامل کاربر را به تأخیر می‌اندازد. کاهش این تسک‌ها، یک اقدام پایه است.

چهارمین اشتباه، استفاده بی‌مورد از جاوااسکریپت برای انیمیشن‌ها است. انیمیشن‌های CSS در لایه کامپوزیت اجرا می‌شوند و رشته اصلی را بلاک نمی‌کنند.

پنجمین اشتباه، نبود تنظیم passive: true روی event listenerهای اسکرول و لمس است. بدون این تنظیم، مرورگر منتظر پاسخ handler می‌ماند و این انتظار، تأخیر ایجاد می‌کند.

ششمین اشتباه، بی‌توجهی به Layout Thrashing است. تداخل خواندن و نوشتن در DOM، باعث محاسبه مکرر چیدمان می‌شود که زمان پردازش را افزایش می‌دهد.

هفتمین اشتباه، بارگذاری همه اسکریپت‌ها در همه صفحات است. اسکریپت‌های خاص یک صفحه، باید تنها در همان صفحه بارگذاری شوند.

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

نهمین اشتباه، نبود اندازه‌گیری مستمر است. INP معیاری پویا است که با تغییرات سایت تغییر می‌کند. اندازه‌گیری مستمر، از انحراف تدریجی جلوگیری می‌کند.

دهمین اشتباه، تمرکز صرف بر لایه فرانت‌اند است. اگرچه INP عمدتاً در لایه فرانت‌اند شکل می‌گیرد، لایه سرور نیز به‌طور غیرمستقیم اثر می‌گذارد. اگر TTFB کند باشد، همه معیارهای دیگر نیز تحت تأثیر قرار می‌گیرند. اصول تفصیلی در بهینه‌سازی TTFB در وردپرس آمده است.

یازدهمین اشتباه، نادیده گرفتن اثر حجم DOM است. در صفحات با تعداد زیاد عناصر، هر رندر مجدد زمان‌بر می‌شود.

دوازدهمین اشتباه، فراموش کردن تست روی دستگاه‌های کم‌توان است. INP در دستگاه‌های ضعیف به‌مراتب بیشتر تحت تأثیر قرار می‌گیرد. تست روی این دستگاه‌ها، تصویر واقع‌بینانه‌تری ارائه می‌دهد. اصول تفصیلی در بهینه‌سازی INP برای موبایل آمده است.

پرسش‌های پرتکرار درباره INP در وردپرس

INP چیست و چه تفاوتی با FID دارد؟

INP معیاری است که همه تعامل‌های کاربر در طول عمر صفحه را می‌سنجد. FID تنها اولین تعامل را اندازه می‌گرفت. INP سه جزء تأخیر ورودی، زمان پردازش و تأخیر ارائه را پوشش می‌دهد، در حالی که FID تنها تأخیر ورودی را می‌سنجید.

مقدار مطلوب INP چقدر است؟

مقدار کمتر از ۲۰۰ میلی‌ثانیه عالی است. مقدار ۲۰۰ تا ۵۰۰ میلی‌ثانیه نیازمند بهبود است. مقدار بیش از ۵۰۰ میلی‌ثانیه ضعیف در نظر گرفته می‌شود.

چرا INP در وردپرس اغلب ضعیف است؟

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

چگونه INP را اندازه‌گیری کنم؟

با استفاده از کتابخانه web-vitals در سایت. با استفاده از داده‌های CrUX در PageSpeed Insights. با استفاده از تب Performance در Chrome DevTools.

آیا INP روی سئو اثر دارد؟

بله، INP بخشی از Core Web Vitals است و به‌عنوان سیگنال کیفیت تجربه صفحه در نظر گرفته می‌شود. بهبود INP، بخشی از مسیر کلی بهبود رتبه است.

آیا INP جایگزین FID شده است؟

بله. FID در سال‌های اخیر به‌تدریج جای خود را به INP داد، چون FID محدودیت‌های قابل توجهی داشت و تنها بخشی از تجربه تعامل کاربر را می‌سنجید.

چگونه INP را در فروشگاه ووکامرس بهبود دهم؟

با کاهش تعداد افزونه‌های فعال، بهینه‌سازی جاوااسکریپت قالب، بارگذاری تأخیری اسکریپت‌های غیرضروری، استفاده از Web Worker برای پردازش‌های سنگین و کاهش حجم DOM در صفحات محصول.

آیا حذف jQuery به بهبود INP کمک می‌کند؟

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

آیا Web Worker به بهبود INP کمک می‌کند؟

بله، Web Worker پردازش‌های سنگین را به یک رشته جداگانه منتقل می‌کند و رشته اصلی را برای تعامل کاربر آزاد می‌گذارد. این رویکرد برای پردازش‌هایی که نیازی به DOM ندارند، مؤثر است.

آیا استفاده از CSS به‌جای جاوااسکریپت برای انیمیشن‌ها کمک می‌کند؟

بله. انیمیشن‌های CSS در لایه کامپوزیت اجرا می‌شوند و نیازی به رشته اصلی ندارند. انتقال انیمیشن‌ها از جاوااسکریپت به CSS، رشته اصلی را آزاد می‌کند.

چگونه تسک‌های طولانی را شناسایی کنم؟

با استفاده از PerformanceObserver و ثبت تسک‌های بیش از ۵۰ میلی‌ثانیه. با استفاده از تب Performance در Chrome DevTools. این ابزارها نقاط بلاک شدن را به‌طور دقیق نشان می‌دهند.

آیا INP در موبایل با دسکتاپ متفاوت است؟

بله. INP در موبایل معمولاً بالاتر است، چون پردازنده‌های موبایل ضعیف‌تر از دسکتاپ هستند و اثر تسک‌های طولانی بیشتر است. این تفاوت باید در بهینه‌سازی لحاظ شود.

آیا کاهش حجم جاوااسکریپت تنها راه بهبود INP است؟

خیر. کاهش حجم یکی از راه‌های بهبود است اما راه‌حل‌های دیگری مانند انتقال پردازش به Web Worker، بهینه‌سازی DOM، استفاده از CSS برای انیمیشن‌ها و بارگذاری تأخیری نیز مؤثر هستند.

چند وقت یک‌بار باید INP را بررسی کنم؟

در سایت‌های پربازدید، بررسی هفتگی یا ماهانه توصیه می‌شود. در سایت‌های با تغییرات مکرر، بررسی پس از هر تغییر عمده در جاوااسکریپت یا افزونه‌ها ضروری است.

آیا افزونه‌های وردپرس بر INP اثر می‌گذارند؟

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

آیا INP برای همه سایت‌ها معیار مهمی است؟

برای سایت‌هایی که تعامل کاربر بالا دارند (فروشگاه، پلتفرم‌های محتوایی، ابزارهای آنلاین) INP معیار مهمی است. برای سایت‌های صرفاً اطلاعاتی، اهمیت آن کمتر است اما همچنان در Core Web Vitals لحاظ می‌شود.

آیا بهبود INP نیازمند بازنویسی کد است؟

در بیشتر موارد، نه. بهبود INP معمولاً با پیکربندی مناسب، بارگذاری تأخیری، شکستن تسک‌های طولانی و انتقال پردازش‌های سنگین قابل دستیابی است.

آیا کد تولیدشده با هوش مصنوعی بر INP اثر دارد؟

بستگی به کیفیت کد دارد. کد ناکارآمد تولیدشده با AI می‌تواند تسک‌های طولانی ایجاد کند و INP را کاهش دهد. اما کد با کیفیت می‌تواند بهینه باشد. مهم، بازبینی و بهینه‌سازی کد است.

آیا استفاده از افزونه کش بر INP اثر دارد؟

افزونه کش معمولاً بر LCP و TTFB اثر می‌گذارد، نه مستقیماً بر INP. اما اگر افزونه کش جاوااسکریپت اضافه‌ای تزریق کند که بر تعامل اثر بگذارد، می‌تواند INP را تحت تأثیر قرار دهد.

آیا INP پس از بارگذاری صفحه اندازه‌گیری می‌شود؟

بله. INP برخلاف LCP و CLS که در زمان بارگذاری اندازه‌گیری می‌شوند، در طول عمر صفحه و در زمان تعامل کاربر سنجیده می‌شود. این تفاوت، INP را به معیاری متمایز تبدیل می‌کند.

یک نکته برای ادامه مسیر

INP معیاری است که رفتار سایت را در لحظه استفاده واقعی می‌سنجد، نه در لحظه بارگذاری اولیه. این ویژگی، آن را به معیاری متمایز تبدیل می‌کند که نیازمند رویکرد متفاوتی در بهینه‌سازی است. تفاوت میان یک سایت سریع و یک سایت پاسخگو، در همین رویکرد نهفته است.

اگر روی پروژه خودتان این مسیر را طی کرده‌اید، خوشحال می‌شوم بدانم کدام بخش بیشترین زمان را گرفت: شناسایی تسک‌های طولانی، بهینه‌سازی اسکریپت‌های شخص ثالث، یا هماهنگ‌کردن تغییرات فرانت‌اند با لایه سرور. تجربه‌تان را در دیدگاه‌ها بنویسید؛ به‌ویژه اگر راه‌حل متفاوتی پیدا کرده‌اید که می‌تواند برای خواننده بعدی مفید باشد.