likes
comments
collection
share

了解一下 Partytown 🎉:在 Web Worker 中运行 第三方脚本

作者站长头像
站长
· 阅读数 58

本文为翻译

原文标题:Introducing Partytown 🎉: Run Third-Party Scripts From a Web Worker

原文作者:Adam Bradley

原文地址:dev.to/adamdbradle…

把你的第三方脚本挂在 Partytown 这个充满乐趣的地方吧

对任何 网站 或 Web App 来说,性能调优 都是件大事。毫无疑问,如果一个网站能即时加载,滚动顺畅,还能立即响应任何交互,那肯定能提供非常完美的用户体验。

但 就算你 遵循最佳实践 将页面调优做到极致,一旦添加了 第三方脚本,你在性能优化上付出的心血也会被瞬间抹去,这样的事,我已经屡见不鲜了。我们这里说的 第三方脚本(third-party scripts) 是指那些 嵌入到你网站中,但 不直接受控于你 的 代码。例如,埋点分析,广告追踪脚本,A/B测试,追踪器 等等。

在我们做性能优化的时候,我们通常能够根据 页面的资源量 来推断如何去做优化,但面对 第三方脚本 我们总是束手无策。

第三方脚本 的 性能问题

显而易见,第三方脚本 总会 占用掉大量 主线程上的宝贵资源。确实,我们可以通过一些奇技淫巧来降低他们在前期的破坏性影响,比如,等到页面加载完成后,再延迟执行这些脚本。

但是不管你玩得多花,这些脚本还是会在用户的主线程上执行 几百 kb(甚至常常达到 几 mb) 的 JavaScript!而且你想想,终端用户的 移动设备 可是要比 开发者用于开发的机器 性能更差!这会极大的影响 Lighthouse 跑分核心 Web 指标搜索引擎排名,甚至 由于糟糕的用户体验 会导致 跳出率提升 和 用户参与度下降。

我们在为 Builder.io 打造 Qwik 时,意识到了这些问题。简而言之,我们能让任何交互式站点只加载 HTML 和 CSS,并仅拉取必要的 JavaScript。但无论如何,就算我们用上了最快的框架(或者根本不用框架),让框架发挥极限,第三方脚本也会持续消耗整个网站的性能。所以我们想了些办法 …

在 Web Worker 中运行第三方脚本

Partytown 的核心思想是:主线程应当一心一意的执行你的代码,任何在 关键渲染路径 中 不必要的脚本都应当被迁移到 Web Worker 中。将它们迁移到一个 被沙箱化的位置,这有点像 … 一个为 第三方(third-party)脚本准备的 城镇(town)。我们可以叫他 派对之城(Partytown),你觉得如何 …

多年来,Web Worker 一直是种将 资源密集型任务 从 主线程 上卸下来 的实用解决方案 。然而,真正的挑战在于 worker 没法直接访问 主线程 API,比如 windowdocumentlocalStorage 。我们虽说可以在 worker 和 主线程 之间创建一种通信系统,但由于 postMessage异步 的,这种传统的通信系统 没法保证 充斥着 DOM 操作 的 第三方脚本 正常运行。

例如,下面的代码片段是 Google Tag Manager 中的:

var w = document.body.clientWidth;

这段代码没啥特殊的,甚至可以说有点太常见了。但是注意,它是同步执行的,而且有三次 阻塞的对象属性读取:

  1. 读取 document
  2. 读取 body
  3. 读取 clientWidth

如果我们不能用 Promise 或 回调函数 重构这段代码,那么异步通信系统连让这段代码 “跑起来” 都做不到。我想强调的是,“重构代码这条路走不通”。

在你阅读这篇文章时,同样的第三方脚本 运行在数以亿计的不同设备上,不可能说重构就重构。如果我们活在一个理想化的世界,我会直接给 Google 发消息说:“嘿,你们应该了解那段影响 无数交易的 分析代码吧?将它完全重构一下。谢谢。” 接下来,我还会针对世界上每一个单独的服务,跟它们的厂商进行直接沟通,说服他们重构代码。祝我好运吧,虽说我没法保证每一次都能成功。

带我去 派对之城(Partytown)

Partytown 是一个只有 6kb 的延迟加载库,它 能帮助我们将 资源密集型脚本 迁移到 Web Worker 中,从而将它们与主线程分离。Partytown 的目标是让 主线程 能专心执行你的代码,把第三方脚本卸下来 弄到 Web Worker 里,以此来给网站提速。

但其实 Partytown 最重大的功能是 能让 Web Worker 同步的读取 主线程 里的东西。如果 Web Worker 中运行的代码可以同步调用 阻塞的 DOM API 并得到返回值,那么就意味着 我们可以,在未经修改的前提下,就在 worker 中执行 第三方脚本。第三方脚本代码可以按预期执行,只不过它会运行在不同的线程中,不会和你自己的代码争抢资源。

沙箱化 和 隔离

第三方脚本 一般来说 都是一个充斥着大量 JavaScript 的黑箱。很难说这些 混淆代码里面到底藏了些什么东西。代码被压缩确实有充分的理由,但无论如何,这让我们很难搞清楚 第三方代码 在 你的 网站 和 用户的设备 上做了什么,而且这些第三方代码 还会作为你的应用程序代码 共享同样的 线程/上下文。

Partytown 在另一方面,也能将 第三方脚本 沙箱化 或 隔离 到 Web Worker 中,你可以选择是否允许其 对 主线程 API 的访问。包括 cookies,localStorage,userAgent 等等。因为这些第三方脚本代码 必须 走一遍 Partytown 的代理,才能访问到主线程,Partytown 也有能力记录其每一次读写操作,甚至限制对某些 DOM API 的访问。

从本质上来讲,Partytown 能帮你:

  • 将 第三方脚本 隔离到 沙箱。
  • 为 特定的脚本 配置能够访问那些 浏览器 API。
  • 可以选择记录 API 的 调用 和 调用参数,以此更好的了解到 第三方脚本 到底在做什么。

这在很多场景下都非常有用,比如:

  • 阻止访问 document.cookie
  • 提供一个标准的 navigator.userAgent
  • 禁止脚本写入 localStorage
  • document.write() 失效
  • 阻止脚本请求其他脚本

当前状态 和 下一步计划

Partytown 还处于 alpha 测试阶段,它还是高度实验性质的,尚无法投入生产。然而,我们已经在 Builder.io 的生产环境中积极测试了几个页面,目前看来效果很不错。数据如预期的一样正常收集,分析代码看上去没有被影响。我们现在还在采集数据,在之后的文章中我们会展示出来。

在下一篇文章中,我会集中讨论 同步通信通道 是如何工作的,我们又作出了怎样的权衡。

此外,我们还将展示我们在 React 或 Next.js 项目(或 任何网站 任何 Web App)中是如何测试 Partytown 的。这里有一个小例子,是 Partytown 在 Next.js 的应用,后续文章我会提供更多的内容:

import { Partytown, GoogleTagManager } from '@builder.io/partytown/react';
import Document, { Html, Head, Main, NextScript } from 'next/document';

export default class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head>
          <GoogleTagManager containerId={'GTM-XXXXX'} />
          <Partytown />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

各位,派对开始了!

转载自:https://juejin.cn/post/7048621194743922718
评论
请登录