发布·  

Nuxt Scripts 介绍

Nuxt Scripts 为第三方脚本提供更好的性能、隐私、安全性和开发体验。
Harlan Wilton

Harlan Wilton

@harlan-zw

Nuxt 团队与Chrome Aurora团队(Google)合作,很高兴宣布公测版发布Nuxt Scripts.

Nuxt Scripts 是一种处理第三方脚本的更好方式,提供改进的性能、隐私、安全性和开发体验。

了解 Nuxt Scripts

一年多以前,Daniel 发布了最初的Nuxt Scripts RFC。RFC 提出了一个模块,旨在“允许管理和优化第三方脚本,遵循高性能和合规网站的最佳实践”。

我曾有个人经验处理与第三方脚本相关的性能问题,深知这些性能优化有多么困难。尽管如此,我仍热衷于解决这个问题,并接管了这个项目。

以 RFC 为创意种子,我开始原型化它可能是什么样子使用Unhead.

在思考我究竟想构建什么时,我发现真正的问题不仅仅是如何加载“优化”的第三方脚本,而是如何使与第三方脚本的整体工作体验变得更好。

为什么要构建第三方脚本模块?

94% 的网站使用至少一个第三方提供商,平均每个网站有五个第三方提供商.

我们知道第三方脚本并不完美;它们会拖慢网络速度,导致隐私和安全问题,并且使用起来很麻烦。

然而,它们从根本上是有用的,并且不会很快消失。

通过探究第三方脚本的问题,我们可以看到哪些方面可以改进。

😒 开发体验:全栈式头痛

让我们来看看如何使用虚构的 tracker.js 脚本将第三方脚本添加到您的 Nuxt 应用程序中,该脚本会向 window 添加一个 track 函数。

我们首先使用 useHead 加载脚本。

useHead({ script: [{ src: '/tracker.js', defer: true }] })

然而,现在让我们尝试在我们的应用程序中使脚本功能正常工作。

在 Nuxt 中使用第三方脚本时,以下步骤很常见

  • 一切都必须包裹起来以确保 SSR 安全。
  • 脚本加载后进行不稳定检查。
  • 为类型增强 window 对象。
<script setup>
// ❌ Oops, window is not defined! 
// 💡 The window can't be directly accessed if we use SSR in Nuxt.
// 👉 We need to make this SSR safe
window.track('page_view', useRoute().path)
</script>

🐌 性能:“为什么我 Lighthouse 无法达到 100 分?”

为了让访问者开始与您的 Nuxt 网站互动,需要下载应用程序包,并且 Vue 需要水合应用程序实例。

加载第三方脚本可能会干扰此水合过程,即使使用 asyncdefer 也是如此。这会减慢网络速度并阻塞主线程,导致用户体验下降和较差的核心网页指标.

用于格式化的Chrome 用户体验报告显示,具有大量第三方资源的 Nuxt 网站通常具有较差的下一次绘制的交互时间 (INP)等等最大内容绘制 (LCP)分数。

要了解第三方脚本如何降低性能,我们可以查看2022 年网络年鉴。报告显示,排名前 10 的第三方脚本 平均中位阻塞时间为 1.4 秒

🛡️ 隐私与安全:不作恶?

在排名前 10,000 的网站中,有 58% 的网站使用第三方脚本交换存储在外部 cookie 中的跟踪 ID,这意味着即使禁用第三方 cookie,它们也可以跨网站跟踪用户。

虽然在许多情况下,我们对使用的提供商别无选择,但我们应该尽量减少我们泄露的最终用户数据量。

当我们确实承担了隐私影响时,可能很难在我们的隐私政策中准确传达这些信息,并构建遵守 GDPR 等法规所需的同意管理。

使用第三方脚本时的安全性也是一个问题。第三方脚本是恶意行为者的常见攻击向量,大多数不为其脚本提供 integrity 哈希,这意味着它们随时可能被入侵并向您的应用程序注入恶意代码。

Nuxt Scripts 如何解决这些问题?

可组合项:useScript

这个可组合项位于 <script> 标签和添加到 window.{thirdPartyKey} 的功能之间。

对于 <script> 标签,该可组合项

  • 提供脚本加载和错误状态的完整可见性
  • 默认情况下,在 Nuxt 水合应用程序时加载脚本,以获得略好的性能。
  • 限制 crossoriginreferrerpolicy 以提高隐私和安全。
  • 提供一种延迟加载脚本直到您需要它。

对于脚本 API,它

  • 提供脚本函数的完整类型安全
  • 添加一个代理层,允许您的应用程序在脚本函数在不安全上下文(SSR、脚本加载之前、脚本被阻止)中运行时运行
const { proxy, onLoaded } = useScript('/hello.js', {
  trigger: 'onNuxtReady',
  use() {
    return window.helloWorld
  }
})

onLoaded(({ greeting }) => {
  // ✅ script is loaded! Hooks into Vue lifecycle
})

// ✅ OR use the proxy API - SSR friendly, called when script is loaded
proxy.greeting() // Hello, World!

declare global {
  interface Window {
    helloWorld: {
      greeting: () => 'Hello World!'
    }
  }
}

脚本注册表

用于格式化的脚本注册表是常见第三方脚本的一系列第一方集成。截至发布时,我们支持 21 个脚本,未来还会支持更多。

这些注册表脚本是围绕 useScript 的微调包装器,具有完整的类型安全、脚本选项的运行时验证(仅限开发环境)和环境变量支持

例如,我们可以看看Fathom Analytics脚本。

const { proxy } = useScriptFathomAnalytics({
  // ✅ options are validated at runtime
  site: undefined
})
// ✅ typed
proxy.trackPageview()

外观组件

注册表包含多个外观组件,例如Google 地图, YouTube等等Intercom.

外观组件是“假”组件,它们在第三方脚本加载时进行水合。外观组件有其权衡,但可以极大地提高您的性能。请参阅什么是外观组件?指南了解更多信息。

Nuxt Scripts 提供外观组件,它们可访问但无头,这意味着它们默认不带样式,但会添加必要的 a16y 数据。

点击加载
点击视频将加载 YouTube iframe 并开始播放视频。

useScript 可组合项让您完全控制脚本的加载方式和时间,方法是提供自定义 trigger 或手动调用 load() 函数。

在此基础上,Nuxt Scripts 提供了高级触发器,使其变得更加简单。

  • 同意管理- 仅在用户同意后加载脚本,例如通过 cookie 横幅。
  • 元素事件触发器- 根据用户交互(例如滚动、点击或表单提交)加载脚本。
const cookieConsentTrigger = useScriptTriggerConsent()
const { proxy } = useScript<{ greeting: () => void }>('/hello.js', {
  // script will only be loaded once the consent has been accepted
  trigger: cookieConsentTrigger
})
// ...
function acceptCookies() {
  cookieConsentTrigger.accept()
}
// greeting() is queued until the user accepts cookies
proxy.greeting()

捆绑脚本

在许多情况下,我们从不受我们控制的域加载第三方脚本。这可能导致许多问题

  • 隐私:第三方脚本可以跨站点跟踪用户。
  • 安全:第三方脚本可能被入侵并注入恶意代码。
  • 性能:额外的 DNS 查找会减慢页面加载速度。
  • 开发者体验:已同意的脚本可能会被广告拦截器阻止。

为了缓解这个问题,Nuxt Scripts 提供了一种无需额外工作即可将第三方脚本捆绑到您的公共目录中的方法。

useScript('https://cdn.jsdelivr.net.cn/npm/js-confetti@latest/dist/js-confetti.browser.js', {
  bundle: true,
})

该脚本现在将从您自己的域上的 /_scripts/{hash} 提供。

待续

正如我们所见,第三方脚本仍有许多机会可以为开发者和最终用户提供改进。

Nuxt Scripts 的首次发布解决了其中 一些 问题,但我们仍有大量工作要做。

接下来的路线图项目包括

我们非常乐意得到您的贡献和支持。

入门

要开始使用 Nuxt Scripts,我们创建了一个教程来帮助您快速上手。

鸣谢

并非常感谢早期贡献者。