INP הפך למדד קריטי של Core Web Vitals — מאז מרץ 2024 הוא מחליף את FID ומכסה את כל האינטראקציות של המשתמש. INP גרוע (מעל 200 אלפיות השנייה) מפחית ישירות את ההמרות: כל עיכוב של 100 אלפיות השנייה בקלט או בלחיצה מרחיק 1–2% מהמבקרים. אנו עוזרים ללקוחות להגיע ל-≤200 אלפיות השנייה ומאשרים את התוצאה באמצעות ניטור אינסטרומנטלי, באמצעות שילוב של פיצול Long Tasks, סינון אסינכרוני והעברת חישובים כבדים ל-Web Workers.
איך INP עובד
INP = הזמן מפעולת המשתמש (mousedown, keydown, pointerdown) ועד לציור הפריים הבא בדפדפן. העיכוב מורכב משלושה שלבים: עיכוב קלט — המתנה לשחרור ה-thread הראשי; זמן עיבוד — ביצוע מטפלי האירועים; עיכוב הצגה — layout, paint, composite. תרחיש טיפוסי: המשתמש לוחץ על כפתור סינון, אבל ה-thread הראשי עסוק בניתוח סקריפט אנליטיקה — הלחיצה ממתינה 120 אלפיות השנייה, ואז המטפל רץ 80 אלפיות השנייה, ועוד 50 אלפיות השנייה לרינדור — סה"כ 250 אלפיות השנייה, כבר מעבר לסף המקובל.
למה INP קריטי ל-SEO?
גוגל הפכה את INP לאחד משלושת אותות הדירוג המרכזיים. אתרים עם INP > 200 אלפיות השנייה מאבדים דירוג ומקבלים פחות תנועה אורגנית. למסחר אלקטרוני, כל אלפית שנייה של עיכוב מפחיתה את ההמרה ב-1–2%. בפרויקט אחד, לאחר הפחתת INP מ-350 אלפיות השנייה ל-80 אלפיות השנייה, ההמרה עלתה ב-12%, ועומק הביקור הממוצע גדל ב-2 עמודים.
אבחון אינטראקציות איטיות
השתמשו ב-PerformanceObserver כדי לאסוף את כל האינטראקציות עם עיכוב >16 אלפיות השנייה:
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 200) {
console.warn(`Slow interaction: ${entry.name}`, {
duration: entry.duration,
processingStart: entry.processingStart,
processingEnd: entry.processingEnd,
inputDelay: entry.processingStart - entry.startTime,
processingTime: entry.processingEnd - entry.processingStart,
presentationDelay: entry.startTime + entry.duration - entry.processingEnd,
});
}
}
}).observe({ type: 'event', buffered: true, durationThreshold: 16 });
ב-Chrome DevTools → Performance → הקליטו את העמוד → סננו Long Tasks. כל משימה >50 אלפיות השנייה היא מועמדת לאופטימיזציה. בסשנים אמיתיים אנו רואים לעיתים קרובות Long Tasks מ-100 עד 500 אלפיות השנייה, הנגרמות על ידי סקריפטים של צד שלישי או חישובים כבדים על ה-thread הראשי.
ביטול Long Tasks: שתי גישות
פיצול באמצעות yield מתאים לחישובים קלים. Web Worker עדיף למשימות עתירות CPU מכיוון שהוא משחרר לחלוטין את ה-thread הראשי. מקרה אמיתי: בחנות מקוונת, סינון של 20,000 מוצרים בזמן אמת נתן INP של 350 אלפיות השנייה. העברנו את הסינון ל-Web Worker והוספנו וירטואליזציה של רשימה — INP ירד ל-80 אלפיות השנייה.
// Async filter с yield каждые 50 элементов
async function filterProductsAsync(products, filters) {
const results = [];
for (let i = 0; i < products.length; i++) {
if (matchesFilters(products[i], filters)) {
results.push(products[i]);
}
if (i % 50 === 0 && i > 0) {
await scheduler.yield(); // Chrome 115+
// fallback: await new Promise(r => setTimeout(r, 0));
}
}
return results;
}
// Web Worker – вынос тяжёлой фильтрации
// worker.js
self.onmessage = function({ data: { products, filters } }) {
const results = products.filter(p => matchesFilters(p, filters));
self.postMessage(results);
};
// main.js
const worker = new Worker('/js/filter-worker.js');
worker.postMessage({ products, filters });
worker.onmessage = ({ data }) => setFilteredProducts(data);
אופטימיזציה של רכיבי React
בעיה: סינון סינכרוני על כל הקלדה חוסם את ה-thread הראשי. פתרון — new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.duration > 200) { console.warn(`Slow interaction: ${entry.name}`, { duration: entry.duration, processingStart: entry.processingStart, processingEnd: entry.processingEnd, inputDelay: entry.processingStart - entry.startTime, processingTime: entry.processingEnd - entry.processingStart, presentationDelay: entry.startTime + entry.duration - entry.processingEnd, }); } } }).observe({ type: 'event', buffered: true, durationThreshold: 16 }); ווירטואליזציה. הנה רכיב חיפוש עם משוב מיידי:
function ProductList() {
const [query, setQuery] = useState('');
const [deferredQuery, setDeferredQuery] = useState('');
const filtered = useMemo(
() => products.filter(p => p.name.toLowerCase().includes(deferredQuery.toLowerCase())),
[deferredQuery]
);
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
const value = e.target.value;
setQuery(value); // срочно — поле отзывается мгновенно
startTransition(() => {
setDeferredQuery(value); // некритично — список обновится позже
});
}
return (
<>
<input value={query} onChange={handleChange} />
<ul>
{filtered.map(p => <ProductItem key={p.id} product={p} />)}
</ul>
</>
);
}לרשימות ארוכות, השתמשו בווירטואליזציה:
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualProductList({ products }: { products: Product[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const rowVirtualizer = useVirtualizer({
count: products.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 80,
overscan: 5,
});
return (
<div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
<div style={{ height: rowVirtualizer.getTotalSize() }}>
{rowVirtualizer.getVirtualItems().map(virtualRow => (
<div
key={virtualRow.index}
style={{
transform: `translateY(${virtualRow.start}px)`,
position: 'absolute',
width: '100%',
}}
>
<ProductItem product={products[virtualRow.index]} />
</div>
))}
</div>
</div>
);
} סקריפטים של צד שלישי: איום נסתר
צ'אטים, פיקסלים, אנליטיקה — גורמים נפוצים ל-INP גרוע. הם רצים על ה-thread הראשי וחוסמים אינטראקציות. פתרונות:
- טעינה לאחר התוכן הראשי (setTimeout 3 שניות לאחר הטעינה).
- שימוש ב-Partytown כדי להריץ סקריפטים ב-Web Worker.
טעויות אופייניות באופטימיזציית INP
- אופטימיזציה של אינטראקציה אחת בלבד, בעוד INP לוקח את הגרועה ביותר.
- התעלמות מעיכוב הצגה — לפעמים הציור אורך יותר מהמטפל.
- שימוש ב-
// Async filter с yield каждые 50 элементов async function filterProductsAsync(products, filters) { const results = []; for (let i = 0; i < products.length; i++) { if (matchesFilters(products[i], filters)) { results.push(products[i]); } if (i % 50 === 0 && i > 0) { await scheduler.yield(); // Chrome 115+ // fallback: await new Promise(r => setTimeout(r, 0)); } } return results; } // Web Worker – вынос тяжёлой фильтрации // worker.js self.onmessage = function({ data: { products, filters } }) { const results = products.filter(p => matchesFilters(p, filters)); self.postMessage(results); }; // main.js const worker = new Worker('/js/filter-worker.js'); worker.postMessage({ products, filters }); worker.onmessage = ({ data }) => setFilteredProducts(data);במקוםuseTransition— הראשון לא מבטיח שחרור של ה-thread. - שכחת מובייל: במעבדים חלשים Long Tasks מתרחשים לעיתים קרובות יותר.
איך למדוד INP בזמן אמת?
הקימו Real User Monitoring (RUM) השולח מדדים לאנליטיקה. סננו בוטים באמצעות function ProductList() { const [query, setQuery] = useState(''); const [deferredQuery, setDeferredQuery] = useState(''); const filtered = useMemo( () => products.filter(p => p.name.toLowerCase().includes(deferredQuery.toLowerCase())), [deferredQuery] ); function handleChange(e: React.ChangeEvent<HTMLInputElement>) { const value = e.target.value; setQuery(value); // срочно — поле отзывается мгновенно startTransition(() => { setDeferredQuery(value); // некритично — список обновится позже }); } return ( <> <input value={query} onChange={handleChange} /> <ul> {filtered.map(p => <ProductItem key={p.id} product={p} />)} </ul> </> ); } . דוגמה: אספו את כל האינטראקציות עם INP >200 אלפיות השנייה אל import { useVirtualizer } from '@tanstack/react-virtual'; function VirtualProductList({ products }: { products: Product[] }) { const parentRef = useRef<HTMLDivElement>(null); const rowVirtualizer = useVirtualizer({ count: products.length, getScrollElement: () => parentRef.current, estimateSize: () => 80, overscan: 5, }); return ( <div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}> <div style={{ height: rowVirtualizer.getTotalSize() }}> {rowVirtualizer.getVirtualItems().map(virtualRow => ( <div key={virtualRow.index} style={{ transform: `translateY(${virtualRow.start}px)`, position: 'absolute', width: '100%' }}> <ProductItem product={products[virtualRow.index]} /> </div> ))} </div> </div> ); } ושלחו לשרת לניתוח. זה חושף עמודים ומכשירים בעייתיים.
ערכי יעד של INP
| סוג אינטראקציה | יעד |
|---|---|
| לחיצת כפתור | < 100 אלפיות השנייה |
| קלט בשדה חיפוש | < 150 אלפיות השנייה |
| פתיחת מודאל | < 200 אלפיות השנייה |
| סינון קטלוג | < 200 אלפיות השנייה |
תהליך אופטימיזציית INP
- אבחון — איסוף מדדים אמיתיים באמצעות RUM ובדיקות מעבדה.
- ניתוח — זיהוי 5 האינטראקציות הבעייתיות המובילות עם ערכי עיכוב מדויקים.
- יישום — יישום טכניקות (yield, Workers, טעינה עצלה, וירטואליזציה).
- בדיקות — ניסויי A/B, בדיקת השפעה על המרות ותנועת SEO.
- ניטור — הקמת מעקב רציף אחר INP עם התראות בחריגה מ-200 אלפיות השנייה.
לוח זמנים משוער
מ-3 עד 7 ימים — תלוי במספר האינטראקציות הבעייתיות ובמורכבות הארכיטקטונית. התמחור נקבע באופן אישי לאחר ביקורת.
מעל 50 אופטימיזציות INP מוצלחות. אנו מבטיחים הגעה ל-≤200 אלפיות השנייה או החזר כספי. צרו קשר לביקורת וקבלו תוכנית אופטימיזציה תוך 3 ימים. בקשו ייעוץ — נבחן את המקרה שלכם.







