Nuxt 團隊與 Google 的 Chrome Aurora 團隊合作,很高興宣布公開測試版 Nuxt Scripts。
Nuxt Scripts 是使用第三方腳本的更佳方式,可改善效能、隱私、安全性及開發者體驗。
開始使用 Nuxt Scripts
一年多前,Daniel 發布了最初的 Nuxt Scripts RFC。該 RFC 提議建立一個模組,該模組將「允許管理和最佳化第三方腳本,遵循高效能和合規網站的最佳實務」。
由於在解決與第三方腳本相關的效能問題方面有 個人經驗,我知道這些效能最佳化有多困難。儘管如此,我還是很熱衷於解決這個問題,並接手了這個專案。
以 RFC 作為想法的種子,我開始使用 Unhead 建立它可以 看起來像什麼 的原型。
在思考我究竟想建構什麼時,我發現真正的問題不僅僅是如何載入「最佳化」的第三方腳本,而是如何讓使用第三方腳本的整體體驗更好。
為何要建構第三方腳本模組?
94% 的網站至少使用一個第三方供應商,平均每個網站有 五個第三方供應商。
我們知道第三方腳本並不完美;它們會 拖慢網路速度、引起隱私和安全問題,而且使用起來很麻煩。
然而,它們基本上很有用,而且短期內不會消失。
透過探索第三方腳本的問題,我們可以了解可以在哪些方面進行改進。
😒 開發者體驗:全端令人頭痛的問題
讓我們逐步了解如何使用虛構的 tracker.js
腳本將第三方腳本新增至您的 Nuxt 應用程式,該腳本會將 track
函式新增至視窗。
我們先使用 useHead
載入腳本。
useHead({ scripts: [{ src: '/tracker.js', defer: true }] })
但是,現在讓我們嘗試讓腳本功能在我們的應用程式中運作。
以下是在 Nuxt 中使用第三方腳本時的常見步驟
- 所有內容都必須為了 SSR 安全性而包裝。
- 檢查腳本是否已載入的不可靠方法。
- 擴增視窗物件以取得類型。
<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 需要將應用程式實例水合。
即使使用 async
或 defer
,載入第三方腳本也可能會干擾此水合過程。這會拖慢網路速度並封鎖主執行緒,導致使用者體驗降低和 Core Web Vitals 不佳。
Chrome 使用者體驗報告 顯示,具有大量第三方資源的 Nuxt 網站通常具有較差的 Interaction to Next Paint (INP) 和 Largest Contentful Paint (LCP) 分數。
為了了解第三方腳本如何降低效能,我們可以查看 2022 年網路年鑑。該報告顯示,前 10 大第三方腳本的平均中位數封鎖時間為 1.4 秒。
🛡️ 隱私與安全性:「不作惡」?
在前 10,000 個網站中,有 58% 的網站具有 交換儲存在外部 Cookie 中的追蹤 ID 的第三方腳本,這表示即使停用第三方 Cookie,它們也可以跨網站追蹤使用者。
雖然在許多情況下,我們對於使用的供應商無能為力,但我們應盡可能嘗試最大程度地減少洩露終端使用者資料的數量。
當我們確實承擔隱私影響時,可能難以在我們的隱私權政策中準確傳達這些影響,並建構符合 GDPR 等法規要求的同意管理。
使用第三方腳本時的安全性也是一個問題。第三方腳本是惡意行為者的常見攻擊媒介,大多數第三方腳本未提供其腳本的 integrity
雜湊值,這表示它們隨時可能遭到入侵並將惡意程式碼注入您的應用程式。
Nuxt Scripts 如何解決這些問題?
Composable:useScript
此 composable 位於 <script>
標籤與新增至 window.{thirdPartyKey}
的功能之間。
對於 <script>
標籤,此 composable
- 完全可見腳本的載入和錯誤狀態
- 預設情況下,在 Nuxt 水合應用程式時載入腳本,以獲得稍微更好的效能。
- 限制
crossorigin
和referrerpolicy
以提高隱私和安全性。 - 提供一種方法來 延遲載入腳本,直到您需要它為止。
對於腳本 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()
Facade Components(外觀元件)
註冊表包含多個 外觀元件,例如 Google Maps、YouTube 和 Intercom。
外觀元件是「假的」元件,會在第三方腳本載入時水合。外觀元件具有取捨,但可以大幅提升您的效能。請參閱 什麼是外觀元件? 指南以取得更多資訊。
Nuxt Scripts 提供外觀元件作為可存取但無頭元件,這表示它們預設沒有樣式,但會新增必要的 a16y 資料。

點擊以載入
同意管理與元素事件觸發器
useScript
composable 讓您完全控制腳本的載入方式和時間,方法是提供自訂 trigger
或手動呼叫 load()
函式。
在這種基礎上,Nuxt Scripts 提供進階觸發器,使其更加容易。
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 提供一種將第三方腳本捆綁到您的 public 目錄中的方法,而無需額外的工作。
useScript('https://cdn.jsdelivr.net/npm/js-confetti@latest/dist/js-confetti.browser.js', {
bundle: true,
})
腳本現在將從您自己網域上的 /_scripts/{hash}
提供。
未完待續
正如我們所見,有很多機會可以改善開發者和終端使用者的第三方腳本體驗。
Nuxt Scripts 的初始版本已解決部分這些問題,但我們仍有許多工作要做。
路線圖上的下一個項目是
我們很樂意獲得您的貢獻和支持。
開始使用
為了開始使用 Nuxt Scripts,我們建立了一個教學課程,以協助您快速入門並執行。
鳴謝
- Harlan Wilton - Nuxt (作者)
- Julien Huang - Nuxt (貢獻者)
- Daniel Roe - Nuxt (貢獻者)
- Chrome Aurora - Google (貢獻者)
並衷心感謝早期的貢獻者。