知识广场
按学科筛选:计算机科学 / 前端开发 / 性能
«计算机科学 / 前端开发 / 性能» 分类下共 1 篇帖子
## 起因 你的网站有: - Google Analytics - Google Tag Manager - Facebook Pixel - HubSpot / Intercom chat - Segment 第三方 script 通常 200-500 KB 不少,跑在 main thread → 占 CPU + 拖慢 首屏(hydration / interaction)。 Lighthouse 性能跌 20-30 分常因这些。 `Partytown`(Builder.io):把第三方 script 丢到 web worker 跑。 main thread 专心 render,性能不被第三方坑。 ## 原理 ``` Main thread: - 你的 React / Vue - DOM 操作 - 用户交互 Web Worker (Partytown): - GA / GTM / 等 - 通过 proxy 间接访问 DOM (synchronous via SharedArrayBuffer) ``` 第三方 script 调 `document.cookie` / `window.dataLayer` → Partytown 代理 到 main thread → script 以为自己在 main thread 跑。 ## 装 (Next.js) ```bash npm install @builder.io/partytown ``` ```tsx // next.config.js module.exports = { experimental: { nextScriptWorkers: true }, }; // _document.tsx 或 app/layout.tsx import Script from 'next/script'; <Script strategy="worker" // 关键 src="https://www.googletagmanager.com/gtag/js?id=G-XXX" /> <Script id="gtag-init" strategy="worker"> {` window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'G-XXX'); `} </Script> ``` 跑起来 → GA script 在 worker。 GA 看到的数据跟正常一样。 ## 装 (vanilla / 任意 framework) ```html <head> <script> window.partytown = { forward: ['dataLayer.push'], // 这些 method 转 worker }; </script> <script src="/~partytown/partytown.js"></script> <script type="text/partytown" src="https://www.googletagmanager.com/gtag/js?id=G-XXX"></script> <script type="text/partytown"> window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'G-XXX'); </script> </head> ``` `type="text/partytown"` 是关键 → Partytown 拦截 + 在 worker 跑。 ## 性能效果 我们一个 marketing 站: | metric | before | after | |---|---|---| | Lighthouse Performance | 62 | 92 | | TBT (Total Blocking Time) | 850ms | 80ms | | TTI (Time to Interactive) | 5.2s | 2.1s | | LCP | 3.8s | 2.5s | 主要 GA + GTM + HubSpot 三个第三方加起来约 600 KB JS。 丢 worker 后 main thread 几乎不卡。 ## 副作用 / 限制 1. **不能用 `document.write`**:worker 没 DOM 写。多数现代 script OK。 2. **synchronous DOM access 延迟**:worker → main 调用有几 ms 开销。 GA 类 batched analytics 无感;动画类 script 不行。 3. **某些 script 不兼容**:检测自己环境会发现"不在 main thread" 报错。少数老 script。 4. **需要 `Cross-Origin-*` header**:用 SharedArrayBuffer 优化要 COOP/COEP。 不配也能跑(fallback 慢 IPC)。 ## 哪些第三方适合 适合: - analytics (GA, Mixpanel, Segment, Amplitude) - tag manager (GTM) - chat widget (Intercom, HubSpot) - ad pixel (Facebook, TikTok) - A/B testing (Optimizely) 不适合: - 必须 sync DOM 操作(如 Stripe Elements 表单内嵌) - 视频 / 动画 SDK(需 60fps) - 关键功能 script(auth / payment 核心) ## debug Chrome devtools 看: - main thread:你的 app - worker thread:partytown + 第三方 network tab 看 partytown 跑的 request(带 `partytown` referer)。 ## 与 GTM server-side 对比 GTM server-side container:把 GTM 处理移到自己 server,client 只发原始 event。 | | partytown | GTM SS | |---|---|---| | 复杂度 | 低 | 高 | | client perf | 好 | 极好 | | 成本 | 0 | server cost | | 控制 | 中 | 强 | 大网站常两者结合:partytown for 简单 script + GTM SS for 核心 tracking。 ## 与 facade pattern YouTube embed / chat widget 等:先显示假封面,用户 hover/click 才加载真 iframe。 ```jsx const [loaded, setLoaded] = useState(false); return loaded ? <YouTubeEmbed /> : <FakePoster onClick={() => setLoaded(true)} />; ``` partytown 移走 main thread 加载;facade 直接延迟加载。 互补。 ## 实际接入步骤 1. Lighthouse 跑当前 → 找 main thread blocking script 2. 列出第三方 script 清单 3. 一个一个 wrap partytown → 测 GA / etc 仍正常报数据 4. 重 Lighthouse → 看分提升 通常 1 天内能上线 + 显著效果。 ## 静态站特别合适 Astro / Gatsby / 11ty 等静态站性能"就差第三方拖" → partytown 神药。 Astro 内置 partytown integration: ```js // astro.config.mjs import partytown from '@astrojs/partytown'; export default { integrations: [partytown()] }; ``` ```html <script type="text/partytown" src="..."></script> ``` ## 跟 Next.js Script 对比 Next.js 自己也有 `Script strategy`: - `beforeInteractive`:阻塞,head 内 - `afterInteractive`:default,body 末 - `lazyOnload`:idle 时 - `worker`:partytown(实验) `worker` 内部就是 partytown 集成。 ## 真实 case 某客户 marketing 站 SEO / 性能死活上不去: - GTM 200 KB - HubSpot 300 KB - GA + 5 个 pixel LCP 5s,Google 算"slow" → SEO 罚。 接 partytown 一周: - LCP 2.3s - Core Web Vitals 全绿 - 自然流量 +15%(SEO 改善) 第三方需求没变 → 性能彻底改善。 ## 不要乱开 不是所有 script 都该丢 worker。 仔细想:这 script 真的不需要 sync main thread? 比如 Stripe element 在 form 里要 sync 渲染 → 不行。 GA fire-and-forget event → 完美。 ## 踩过的坑 1. **第三方更新挂掉**:GA 新 SDK 跟 partytown 不兼容(罕见)。 monitor 数据连续性,发现异常 fallback 普通 script。 2. **COOP/COEP header 跟 OAuth iframe 冲突**:需要 cross-origin iframe 的功能可能挂。试个 staging。 3. **dev mode performance 反慢**:partytown 在 dev 调试模式 IPC 开 销大。生产无问题。 4. **dataLayer race condition**:用 partytown 后 dataLayer 是异步 推。需要 sync read 的 code 会拿空。 5. **CSP 配 worker-src**:CSP 严格时 worker 加载被 block。 `worker-src 'self'` 加。