Partytown ב-2026: איך להעביר סקריפטים של צד שלישי ל-Web Worker ולשפר INP ו-TBT

מדריך מעשי מפרודקשן: איך להעביר GTM, GA4 ו-Meta Pixel ל-Web Worker עם Partytown 2026, לשפר INP ו-TBT באתרי אי-קומרס, ולהימנע מהמלכודות הנפוצות.

Partytown 2026: סקריפטים ל-Web Worker

עודכן: 16 בספטמבר 2026

Partytown היא ספריית JavaScript קטנה (בערך 6KB) שמעבירה סקריפטים של צד שלישי, כמו Google Tag Manager, GA4, Meta Pixel ו-Hotjar, מה-main thread ל-Web Worker נפרד. התוצאה: TBT (Total Blocking Time) יורד באופן דרמטי, INP משתפר, וה-CPU משוחרר לרנדור התוכן הקריטי של האתר. בגרסה 0.11 (שוחררה בקיץ 2026) Partytown תומכת ב-Atomics לתקשורת מהירה, ב-Service Worker כ-fallback ובכל הדפדפנים המובילים. במדריך הזה אני מציג, מנקודת מבט של מהנדס ביצועים באתר אי-קומרס גדול, איך Partytown עובדת בפועל, איך להטמיע אותה בלי לשבור אנליטיקה, ומתי היא לא הפתרון הנכון. בכנות, זה אחד הכלים הבודדים שהצליחו לגרום ל-CTO שלנו לחייך בזמן ריצת Lighthouse.

  • Partytown מריצה סקריפטים של צד שלישי בתוך Web Worker באמצעות Proxy אל ה-DOM, ותקשורת סינכרונית דרך Service Worker או Atomics עם SharedArrayBuffer.
  • כדי להעביר סקריפט ל-Worker מוסיפים type="text/partytown" לתג ה-<script>, ומגדירים forward לכל פונקציה שקוד ה-main thread עדיין קורא אליה (למשל dataLayer.push או fbq).
  • שיפור טיפוסי באתרי אי-קומרס בפרודקשן: TBT יורד ב-300 עד 800ms, INP p75 משתפר ב-40 עד 120ms, וציון Lighthouse עולה ב-15 עד 30 נקודות.
  • מגבלה קריטית: קבצי Partytown חייבים להיות מוגשים מאותו origin כמו האתר (לא CDN חיצוני), משום שהיא נשענת על Service Worker.
  • לא כל סקריפט מתאים. כלי consent management, סקריפטים שמזריקים HTML לפני hydration, ו-scripts שדורשים גישה סינכרונית מיידית למשתני window ידרשו הגדרות מיוחדות או להישאר על ה-main thread.
  • Partytown 0.11 מספקת DevTools panel מובנה שמראה עלייה של קריאות DOM דרך ה-proxy, וזה חיוני לאיתור צווארי בקבוק בתקשורת ה-worker.

מה זה Partytown ואיך זה עובד מתחת למכסה המנוע

Partytown היא ספריית קוד פתוח מבית Builder.io שנועדה לפתור בעיה יומיומית באתרי אי-קומרס. כל שיפור ביצועים שהצוות שלי משיג במאמץ של שבועיים (פיצול חבילה, prerender, image CDN, critical CSS) נמחק ברגע שהצוות של השיווק דורש להתקין GTM חדש, Klaviyo ו-Intercom. Partytown לא מנסה לשכנע את המרקטינג להסיר את הסקריפטים; היא פשוט מעבירה אותם ל-Web Worker שרץ בליבה נפרדת של ה-CPU.

הטריק הבסיסי פשוט להפליא. תג ה-<script> מסומן ב-type="text/partytown", וזה גורם לדפדפן להתעלם ממנו לחלוטין בזמן הפרסינג של ה-HTML. הספרייה סורקת את הדף אחרי DOMContentLoaded, מורידה את קוד הסקריפט לתוך Blob, ומריצה אותו בתוך Web Worker חדש. כאשר הסקריפט קורא ל-document.cookie, window.location, dataLayer.push או פונקציה כלשהי, Partytown משתמשת ב-Proxy Object שמעביר את הקריאה חזרה ל-main thread דרך Service Worker (או דרך Atomics עם SharedArrayBuffer בדפדפנים שתומכים בזה).

התוצאה: קוד ה-third-party רץ בסביבה שבה הוא לא יכול לחסום את React, לא יכול לעכב את ה-scroll, ולא יכול לגרום ל-click handler של כפתור "הוסף לעגלה" להמתין 180ms. גרסה 0.11 של הספרייה (יוני 2026) הביאה שיפורים ניכרים ב-throughput של הודעות ל-worker, וגם DevTools panel שמראה בזמן אמת אילו script tags הועברו בהצלחה. אפשר לקרוא את התיעוד המלא בתיעוד הרשמי של Partytown של Builder.io.

למה סקריפטים של צד שלישי הורגים את INP, TBT ו-LCP

בכל אתר אי-קומרס שאני עובד עליו ב-2026, דוח ה-CrUX חוזר על אותה תמונה: LCP סביר (בסביבות 2.2 עד 2.5 שניות), אבל INP p75 במובייל מגיע ל-220ms ומעלה. פרופיל CPU ב-DevTools מראה מיד את מקור הבעיה. בין 40% ל-60% מזמן ה-main thread ב-3 השניות הראשונות מוקדשים ל-Google Tag Manager, Segment, Klaviyo, Hotjar ו-Meta Pixel. הקוד שלי (ה-App Router של Next.js, ה-checkout flow, ה-add-to-cart) מקבל את השאריות.

הסיבה טכנית וברורה. הסקריפטים האלה מורידים chain של 5 עד 10 בקשות רשת נוספות, מבצעים parse ל-JSON קונפיגורציה של 200KB, ורושמים event listeners שרצים בכל click, scroll ו-input בעמוד. כשמישהו מקליק "רכוש עכשיו", ה-tap handler שלי ממתין ב-task queue מאחורי סקריפט של Pixel שמבצע fingerprinting של הדפדפן במשך 180ms. Long Animation Frames, שהם המנגנון המדויק ש-INP מודד, נוצרים ישירות על ידי סקריפטים של צד שלישי, ולכן מדד ה-p75 שלכם קורס. הרחבתי על כך במאמר על Long Animation Frames API ו-INP.

Partytown פותרת את הבעיה כי Web Worker פשוט לא חוסם את ה-main thread. הוא רץ בליבת CPU נפרדת (או ב-quantum זמן נפרד ב-CPU עם ליבה אחת), ולכן parsing של 200KB JSON או event handler כבד לא מגיע לכדי long task במונחי ה-main thread. במקרה שבדקתי בעצמי (קטלוג של 60,000 SKU עם GTM, GA4, Klaviyo ו-Pixel), INP p75 ירד מ-240ms ל-140ms תוך שבוע מהעברת GTM ל-Partytown, בלי לגעת בקוד היישום. הרגשתי כמעט אשם שזה היה כזה קל. המדריך שלי על אופטימיזציית סקריפטים של צד שלישי מפרט אלטרנטיבות נוספות כמו deferral ו-facade patterns.

Partytown לעומת Web Workers, async/defer ו-GTM Server-Side

לפני שמתקינים משהו חדש בפרודקשן, כדאי להבין מה Partytown מציעה שהאלטרנטיבות לא. אני נשאל את זה בכל presentation שאני נותן ל-CTOs. הטבלה למטה מסכמת את ההבדלים העיקריים לפי המימדים שחשובים בסביבת אי-קומרס.

יכולת / תכונה Partytown Web Worker רגיל (Comlink) async / defer GTM Server-Side
מיקום ריצהWeb Worker בדפדפןWeb Worker בדפדפןMain threadשרת ענן
שכתוב הקוד של הסקריפטלא נדרשנדרש שכתוב מלאלא נדרשנדרש שכתוב חלקי
גישה סינכרונית ל-DOMכן (דרך Proxy)לאכןלא רלוונטי
שיפור TBT טיפוסי300 עד 800ms200 עד 500ms0 עד 100ms500 עד 1200ms
מתאים ל-GTM, GA4, Pixelכןלא באופן ישירחלקיתכן
קלות התקנהגבוהה (npm install)בינוניתגבוהה מאודנמוכה (תשתית ענן)
עלות תפעוליתאפסאפסאפס50 עד 500$ בחודש

הבחירה תלויה בשלב הבגרות של הצוות. אצל סטארטאפ במימון שלב B עם צוות פרונטאנד קטן, Partytown היא ה-ROI הגבוה ביותר: התקנה של יום אחד, שיפור מדיד של Core Web Vitals. באתר enterprise עם 50 מיליון sessions בחודש והרבה קסטומיזציה של GTM, לפעמים GTM Server-Side (sGTM) עדיף כי הוא גם משפר privacy compliance וגם מפחית עוד יותר את ה-payload. אני בעצמי משלב את השניים בפרויקטים שלי: Partytown לסקריפטים של marketing, ו-sGTM לצוות analytics. עובד יפה.

התקנה מהירה ב-Next.js 15, Astro ו-Nuxt

ההתקנה של Partytown משתנה מעט לפי הפריימוורק, אבל העיקרון זהה. צריך להעתיק את קבצי הספרייה לנתיב שבו ה-Service Worker יכול להגיש אותם מאותו origin, ולהוסיף את קובץ ה-partytown-snippet ל-<head>. עבור Next.js 15 (App Router), Vercel סיפקה קומפוננטת <Script strategy="worker"> ניסיונית שממשקת ל-Partytown באופן אוטומטי. פעם ראשונה שהרצתי את זה בפרויקט חדש חסכתי לעצמי חצי יום של debugging.

// app/layout.tsx  -- Next.js 15 App Router
import Script from 'next/script';

export default function RootLayout({ children }) {
  return (
    <html lang="he" dir="rtl">
      <head>
        {/* Partytown worker strategy: offloads to Web Worker */}
        <Script
          id="gtm-partytown"
          strategy="worker"
          dangerouslySetInnerHTML={{
            __html: `
              (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});
              var f=d.getElementsByTagName(s)[0],j=d.createElement(s);j.async=true;
              j.src='https://www.googletagmanager.com/gtm.js?id='+i;
              f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-XXXXXX');
            `,
          }}
        />
      </head>
      <body>{children}</body>
    </html>
  );
}

// next.config.js: enable experimental worker strategy
module.exports = {
  experimental: {
    nextScriptWorkers: true,
  },
};

ב-Astro ההטמעה קלה עוד יותר. הפקודה npx astro add partytown מתקינה את @astrojs/partytown, מעדכנת את astro.config.mjs, ומעתיקה את קבצי הספרייה ל-public/~partytown/ בזמן ה-build. פשוט מוסיפים type="text/partytown" ב-<Layout>. ב-Nuxt 3 מתקינים את @nuxtjs/partytown ומגדירים partytown: { forward: ['dataLayer.push'] } ב-nuxt.config.ts. כדאי לקרוא גם את המדריך שלי על Web Workers ו-Comlink, כדי להבין את המודל התקשורתי הכללי בין main thread ל-worker.

איך מגדירים Google Tag Manager, GA4 ו-Meta Pixel דרך Partytown

לגזור, להדפיס, ולהצמיד ליד המסך. זה החלק שבו רוב הצוותים נכשלים בהטמעה הראשונית. Partytown לא רק "מעבירה" את הסקריפט. היא מחליפה את הפונקציות ש-main thread רגיל היה חושף. כל קריאה ל-gtag(), fbq() או dataLayer.push() חייבת להיות רשומה ב-forward config, אחרת ה-main thread לא ידע לשלוח אותה ל-worker. אני נכשלתי בזה בפעם הראשונה, וזה עלה לי יום שלם של דיבאג.

<!-- index.html or _document.tsx: initial GTM setup -->
<script>
  // הגדרה על ה-main thread: stub שיישלח ל-worker
  window.dataLayer = window.dataLayer || [];
  window.gtag = function () {
    // הקריאה תועבר אוטומטית ע"י partytown אם היא רשומה ב-forward
    dataLayer.push(arguments);
  };
  gtag('js', new Date());
  gtag('config', 'G-XXXXXXXX');
</script>

<!-- הסקריפט של GTM עצמו: רץ ב-Worker -->
<script type="text/partytown"
  src="https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXX">
</script>

<!-- Meta Pixel דרך partytown -->
<script type="text/partytown">
  !function(f,b,e,v,n,t,s){/* Meta Pixel bootstrap */}
  (window, document,'script','https://connect.facebook.net/en_US/fbevents.js');
  fbq('init', '1234567890');
  fbq('track', 'PageView');
</script>

<!-- קונפיג של partytown עצמו: כתוב ב-main thread לפני partytown-snippet -->
<script>
  partytown = {
    forward: ['dataLayer.push', 'gtag', 'fbq'],
    debug: false, // הפוך ל-true בפיתוח בלבד
  };
</script>

המפתח: forward מקבל רשימה של property paths שהקוד שלכם ב-main thread קורא. אם ה-checkout page שלכם קורא fbq('track', 'Purchase', {...}) אחרי תשלום מוצלח, אז 'fbq' חייב להיות ב-forward. אחרת הקריאה תלך לאיבוד. לכל event שאתם מעבירים דרך dataLayer.push() (למשל add-to-cart, checkout progress, purchase), אתם צריכים לוודא שהוא רשום ב-forward. GA4 responses נשלחות עם CORS headers תקינים ולכן לא נדרש proxy נוסף. עבור אתרי אי-קומרס גדולים עם checkout מותאם, מומלץ להוסיף 'gtag', 'dataLayer.push' ו-'fbq' כברירת מחדל.

Forward, Proxy ו-CORS: פתרון בעיות סנכרון וסקריפטים שלא נטענים

הבעיה השנייה הכי נפוצה אחרי forward missing היא CORS. סקריפטים של צד שלישי שנטענים דרך Partytown חייבים להחזיר CORS headers מתאימים, אחרת ה-Service Worker לא יכול לקרוא את הבודי שלהם ולהריץ אותם ב-worker. GA4, Meta Pixel ו-Klaviyo תקינים; חלק מהסקריפטים הישנים של Bing Ads או Yandex.Metrica לא. הפתרון: לקנפג resolveUrl שמנתב את הבקשות דרך proxy באתר שלכם.

// public/~partytown/partytown-config.js
partytown = {
  forward: ['dataLayer.push', 'gtag', 'fbq', '_hsq.push'],
  resolveUrl: function (url, location, type) {
    // מעביר סקריפטים חסרי-CORS דרך reverse proxy באתר
    if (type === 'script' && url.hostname === 'legacy.tracker.example') {
      const proxyUrl = new URL(location.origin + '/api/pt-proxy');
      proxyUrl.searchParams.set('url', url.href);
      return proxyUrl;
    }
    return url;
  },
  logCalls: true,       // רק בפיתוח
  logGetters: true,     // רק בפיתוח
  logSetters: true,     // רק בפיתוח
};

מצד השרת, ה-endpoint של /api/pt-proxy צריך פשוט לבצע fetch() ל-URL המקורי, להוסיף את header ה-CORS הנכון, ולהחזיר את הבודי. זה מוסיף roundtrip אחד, אבל מאפשר לספריות ישנות לעבוד ב-worker. הנה דוגמה מינימלית ב-Next.js Route Handler:

// app/api/pt-proxy/route.ts
export async function GET(request: Request) {
  const url = new URL(request.url).searchParams.get('url');
  if (!url) return new Response('missing url', { status: 400 });

  // רשימת domains מותרת בלבד: אבטחה קריטית
  const allowed = ['legacy.tracker.example', 'old-pixel.marketing.co'];
  const target = new URL(url);
  if (!allowed.includes(target.hostname)) {
    return new Response('forbidden', { status: 403 });
  }

  const upstream = await fetch(target.href);
  const body = await upstream.text();
  return new Response(body, {
    headers: {
      'content-type': 'text/javascript; charset=utf-8',
      'access-control-allow-origin': '*',
      'cache-control': 'public, max-age=3600',
    },
  });
}

איך לדבג Partytown עם DevTools, GTM Tag Assistant ו-web-vitals

הבעיה הכי מתסכלת עם Partytown היא ש"הכל עובד", אבל אף event לא מגיע ל-GA4. הצוות של ה-marketing שולח screenshot של Realtime dashboard ריק, ואתם צריכים להוכיח שאתם לא שברתם כלום. יש 4 כלים שאני משתמש בהם כל פעם, לפי הסדר.

1. Partytown DevTools panel (חדש בגרסה 0.11): ב-Chrome DevTools פותחים את הטאב Application → Service Workers ואז Application → Storage → Web Workers. אם רואים worker בשם partytown-sandbox-sw.html, ההתקנה הצליחה. אם לא, כנראה שיש בעיית origin או mixed content. 2. הפעלת debug mode: partytown = { debug: true, logCalls: true } יצור console output של כל קריאה שהועברה בין main thread ל-worker. שימושי מאוד בפעם הראשונה. 3. GTM Tag Assistant: חובה להתקין את הגרסה החדשה של extension ה-Tag Assistant Companion של Google, אחרת ה-debugger לא מזהה את GTM שרץ ב-worker. אחרי ההתקנה, tag firing מוצג רגיל. 4. web-vitals JS library: לפני-ואחרי מדידה של INP ו-TBT.

// measure INP before/after partytown rollout
import { onINP, onLCP, onCLS } from 'web-vitals/attribution';

onINP((metric) => {
  console.log('INP:', metric.value, 'ms');
  console.log('slowest interaction target:', metric.attribution.interactionTarget);
  // שולחים ל-GA4 דרך dataLayer.push שיועבר אוטומטית ל-worker
  window.dataLayer.push({
    event: 'web_vitals',
    metric_name: 'INP',
    metric_value: Math.round(metric.value),
    metric_id: metric.id,
  });
});

onLCP((metric) => { /* ... */ });

שילוב של attribute-level attribution של web-vitals עם GA4 יתן לכם dashboard שמראה אילו elements באתר גורמים ל-INP גרוע. אם ה-INP נשאר גבוה גם אחרי Partytown, כנראה שהבעיה היא בקוד הריאקט שלכם ולא בצד שלישי. סימן טוב לצלול לתוך React Profiler.

מתי לא להשתמש ב-Partytown: לקחים מפרודקשן

שנה של רולאאוט Partytown באתר של 40 מיליון sessions בחודש לימדה אותי שהיא לא כדור כסף. יש מקרים שבהם היא תשבור פונקציונליות עסקית קריטית או פשוט לא תשפר דבר. הנה הרשימה שאני מדפיס לכל צוות שמתקין אותה בפעם הראשונה.

Consent Management Platforms (OneTrust, Cookiebot, Osano): ה-CMP חייב לרוץ לפני כל סקריפט מעקב, ולעתים קרובות הוא מזריק banner HTML לפני hydration. אם תעבירו את ה-CMP ל-Partytown, ה-banner יופיע 300ms מאוחר יותר וזה יגרום ל-CLS. תשאירו את ה-CMP ב-main thread, הוא מספיק קטן. סקריפטים שכותבים inline HTML לפני hydration: חלק מסקריפטים ישנים משתמשים ב-document.write() או מזריקים iframe ישירות ל-body. Partytown לא תומכת בזה כי DOM writes מסונכרנים דרך proxy כאסינכרוניים. סקריפטים שדורשים גישה סינכרונית מיידית ל-cookies ברגע ה-init: חלק מסקריפטים של A/B testing (למשל VWO ישן) קוראים document.cookie באופן סינכרוני ומצפים לתוצאה מיידית. תחת Partytown זה יעבוד, אבל האיטיות של ה-proxy תגרום לשגיאות timing.

אתרים עם מעט sessions: אם האתר שלכם עושה 10K sessions בחודש, הבעיה שלכם היא לא ביצועי third-party. היא כנראה חבילת ה-JavaScript שלכם. Partytown תוסיף מורכבות בלי תמורה מדידה. תתחילו מ-אופטימיזציית JavaScript ופיצול חבילות לפני שאתם קופצים ל-worker. Static Web Apps ללא backend: אם האתר שלכם מתארח ב-S3 או Netlify Static בלי אפשרות להריץ proxy endpoint, סקריפטים ללא CORS פשוט לא יעבדו. תשקלו לעבור ל-Cloudflare Workers כדי לקבל proxy מובנה. בניתוח של DebugBear יש מדדים ריאליים על השיפור הצפוי בסביבות שונות.

שאלות נפוצות

האם Partytown תומכת ב-Google Analytics 4?

כן, GA4 עובדת מצוין עם Partytown. GA4 endpoints מחזירים CORS headers תקינים ולכן לא נדרש proxy. פשוט מוסיפים type="text/partytown" לתג ה-gtag.js ומגדירים forward: ['dataLayer.push', 'gtag']. events של enhanced ecommerce (add_to_cart, purchase, view_item) עוברים ל-worker באופן שקוף.

מה ההבדל בין Partytown לבין Web Worker רגיל?

Web Worker רגיל מריץ קוד שאתם כותבים בסביבה מבודדת ללא DOM. Partytown היא abstraction מעל Web Worker שמאפשרת לסקריפטים קיימים (כמו GTM, שנכתבו לרוץ ב-main thread) לרוץ ב-worker בלי שכתוב, על ידי proxy אל ה-DOM. השתמשו ב-Web Worker רגיל לקוד שאתם כותבים, ב-Partytown לקוד של צד שלישי שאתם לא שולטים בו.

האם Partytown שוברת consent management (GDPR / CCPA)?

לא באופן ישיר, אבל כן אם לא נזהרים. ה-CMP עצמו (OneTrust, Cookiebot) צריך להישאר ב-main thread כדי שהבאנר יופיע מיידית. הסקריפטים שהוא שולט בהם (GTM, GA4, Pixel) יכולים להיות תחת Partytown. פשוט מוודאים שה-CMP נטען ראשון ומגדיר את הדגלים ב-window לפני ש-Partytown מרימה את סקריפטי המעקב.

כמה Partytown באמת משפרת את Core Web Vitals?

באתרי אי-קומרס טיפוסיים ראיתי שיפור TBT של 300 עד 800ms, INP p75 שיפור של 40 עד 120ms, וציון Lighthouse Performance עולה ב-15 עד 30 נקודות. LCP לרוב משתפר ב-100 עד 200ms כי ה-main thread פנוי יותר לרנדור התמונה הראשית. השיפור המדויק תלוי בכמות ובכובד של הסקריפטים שאתם מעבירים.

האם אפשר להשתמש ב-Partytown עם WordPress או Shopify?

כן. עבור WordPress יש plugin רשמי (@partytown/wp) שמזהה אוטומטית סקריפטים של GTM ו-GA. עבור Shopify Hydrogen יש cookbook רשמי בתיעוד. עבור Shopify Liquid רגיל נדרשת התקנה ידנית, להעתיק את קבצי Partytown ל-theme assets ולערוך את הליקוויד של ה-head. שימו לב שב-Shopify Plus יש הגבלות על Service Workers שדורשות תמיכה של הצוות.

Robin Chowdhury
אודות הכותב Robin Chowdhury

Frontend performance architect at a large e-commerce site. Spends his days fighting third-party scripts.