透過 100+ 個技巧的集合學習 Nuxt!

vitalizer
nuxt-vitalizer

立即改善 Google Lighthouse 中的 LCP 分數

Nuxt Vitalizer module

Nuxt Vitalizer

作為一個專注做好一件事Nuxt 模組,提供一系列解決方案,以優化 Google Lighthouse 和 Google PageSpeed Insights 中的最大內容繪製 (LCP)。

此模組為以下 Nuxt 問題(以及其他問題)提供了解決方案

功能

  • 🚀 零配置即可改善 LCP
  • 🫸 移除阻礙渲染的 CSS
  • 🔥 DelayHydration 組件以減少「封鎖時間」指標
  • 💨 SkipHydration 組件保留 SSR 內容以用於初始渲染

設定

npx nuxi@latest module add nuxt-vitalizer

用法

將 Nuxt Vitalizer 加入您的 Nuxt 設定,即可開始使用

// `nuxt.config.ts`
export default defineNuxtConfig({
  modules: ['nuxt-vitalizer']
})

若要自訂模組,請在您的 Nuxt 設定中設定 vitalizer 選項

// `nuxt.config.ts`
export default defineNuxtConfig({
  modules: ['nuxt-vitalizer'],

  vitalizer: {
    // Remove the render-blocking entry CSS
    disableStylesheets: 'entry'
  }
})

LCP 優化功能

套用此模組的優化功能後,您可以達到更高的 Lighthouse 效能分數

Lighthouse SEO performance score when using the module

!注意 此功能預設為啟用。

大型 Nuxt 應用程式可能會因為 HTML 中累積了 <link rel="prefetch"> 標籤,而在 Lighthouse 和 Google PageSpeed Insights 中效能分數不佳。

對於每個動態引入,例如非同步組件和其他資源(如圖片),都會渲染一個 prefetch 連結。這會導致瀏覽器預先載入這些 chunks,即使目前頁面不需要這些 chunks。雖然這對於應用程式的整體效能來說很好,但可能會導致大量的 prefetch 請求,進而對最大內容繪製分數產生負面影響。

此模組會 Hook 到 Nuxt 建置流程中,透過停用動態引入的 prefetch 連結的渲染來優化 LCP 分數。

!注意 此功能必須手動啟用。

Preload 連結用於預先載入目前頁面所需的關鍵資源。雖然它們通常在優化網站效能方面佔有一席之地,但如果使用不當,也可能導致大量的請求。移除 preload 連結可以幫助改善 FCP(首次內容繪製)分數,尤其是在網路狀況不佳的情況下。

若要移除預先載入建置資源,請將 disablePrefetchLinks 選項設定為 true

// `nuxt.config.ts`
export default defineNuxtConfig({
  modules: ['nuxt-vitalizer'],

  vitalizer: {
    disablePrefetchLinks: true
  }
})

停止阻礙渲染的 CSS

!注意 此功能必須手動啟用。為了使用此功能,您需要啟用 Nuxt 的 inlineStyles 功能。啟用此選項後,請務必測試您的應用程式。

CSS 樣式表是阻礙渲染的資源,這表示瀏覽器必須先下載並解析 CSS,才能渲染頁面。透過使用內嵌樣式而不是載入樣式表,瀏覽器可以更快地渲染頁面,進而改善 LCP 分數。

雖然最新的 Nuxt 版本在 SSR 渲染期間會內嵌樣式,但 entry.<hash>.css 樣式表仍然會在 HTML 中渲染。這可能會導致阻礙渲染的 CSS,進而對最大內容繪製分數產生負面影響。

為什麼會這樣呢?正如 Nuxt 核心團隊成員 @danielroe 所解釋的

我認為這是目前內嵌樣式實作的限制。

應用程式中到處使用的樣式可以安全地從 CSS 原始碼中完全移除。但是僅在一個組件或頁面中使用的 CSS 需要位於 CSS 檔案中,並且也需要內嵌。

目前,vite 完全負責在客戶端載入 CSS,這表示即使我們追蹤了已載入的 CSS,也無法阻止 vite 載入包含重複 CSS 的 CSS 檔案。

這是我絕對希望看到被修復的事情。

首先,嘗試在 app.vue 檔案中匯入主要的應用程式樣式。當 Nuxt 建置時,它們將會儲存為 entry CSS 檔案

// `app.vue`
import '~/assets/css/main.css'

現在,將 disableStylesheets 選項設定為 entry,以防止 entry.<hash>.css 樣式表在 HTML 中渲染

// `nuxt.config.ts`
export default defineNuxtConfig({
  modules: ['nuxt-vitalizer'],

  vitalizer: {
    disableStylesheets: 'entry'
  }
})

組件

DelayHydration

!警告 延遲組件的 hydration 是一種技巧,用來欺騙 Lighthouse,使其認為頁面比實際更早進入互動狀態。它可能無法提供實際的效能提升,應謹慎使用。

延遲 hydration 是一種技巧,用來暗示 Lighthouse 頁面比實際更早進入互動狀態。這可以改善 Lighthouse 和 Google PageSpeed Insights 中的「封鎖時間」指標。

DelayHydration 組件是一個簡單的組件,它會在 hydration 組件之前等待一段時間。當您有很多網路請求正在發生,並且想要延遲組件的 hydration 直到網路請求完成時,這會很有用。

組件用法

在您的 Vue 組件中使用 DelayHydration 組件

<template>
  <div>
    <DelayHydration>
      <!-- Ensure to lazy load the component -->
      <LazyMyExpensiveComponent />
    </DelayHydration>
  </div>
</template>

設定

您可以在 vitalizer 模組選項中設定 DelayHydration 組件

// `nuxt.config.ts`
export default defineNuxtConfig({
  modules: ['nuxt-vitalizer'],

  vitalizer: {
    delayHydration: {
      hydrateOnEvents: ['mousemove', 'scroll', 'keydown', 'click', 'touchstart', 'wheel'],
      idleCallbackTimeout: 8000,
      postIdleTimeout: 4000
    }
  }
})
  • hydrateOnEvents 選項指定應觸發 hydration 的事件。預設情況下,當使用者移動滑鼠、滾動、按下按鍵、點擊、觸碰螢幕或滾動滑鼠滾輪時,組件會立即 hydration。
  • idleCallbackTimeout 選項指定等待閒置回呼時,以毫秒為單位的最大等待時間。當有很多網路請求正在發生時,這會很有用。
  • postIdleTimeout 選項指定在閒置回呼之後,以毫秒為單位,在 hydration 組件之前要等待的時間。

SkipHydration

SkipHydration 組件只是在初始渲染時,阻止其子組件在客戶端進行 hydration。換句話說,只要組件未卸載,SSR 內容就會被保留。當第一次訪問頁面時,不需要 hydration 特定組件以節省載入組件 chunk 時,這會很有用。

當導航到新頁面時,SkipHydration 組件將會在客戶端掛載其子組件,並且行為就像一般的 Vue 組件一樣。

組件用法

在您的 Vue 組件中使用 SkipHydration 組件

<template>
  <div>
    <SkipHydration>
      <!-- Ensure to lazy load the component -->
      <LazyMyExpensiveComponent />
    </SkipHydration>
  </div>
</template>

模組選項

interface ModuleOptions {
  /**
   * Whether to remove prefetch links from the HTML. If set to `dynamicImports`, only dynamic imports will be removed. To disable all prefetching, such as images, set to `true`.
   *
   * @remarks
   * This will prevent the browser from downloading chunks that may not be needed yet. This can be useful for improving the LCP (Largest Contentful Paint) score.
   *
   * @default 'dynamicImports'
   */
  disablePrefetchLinks?: boolean | 'dynamicImports'

  /**
   * Whether to remove preload links from the HTML. This can be useful for improving the FCP (First Contentful Paint) score, especially when emulating slow network conditions.
   *
   * @default false
   */
  disablePreloadLinks?: boolean

  /**
   * Whether to remove the render-blocking stylesheets from the HTML. This only makes sense if styles are inlined during SSR rendering. To only prevent the `entry.<hash>.css` stylesheet from being rendered, set to `entry`. If set to `true`, all stylesheet links will not be rendered.
   *
   * @remarks
   * This requires to have the Nuxt `inlineStyles` feature enabled. Make sure to test your application after enabling this option.
   *
   * @default false
   */
  disableStylesheets?: boolean | 'entry'

  /**
   * Options for the `DelayHydration` component.
   */
  delayHydration?: {
    /**
     * Specify the events that should trigger hydration.
     *
     * @default ['mousemove', 'scroll', 'keydown', 'click', 'touchstart', 'wheel']
     */
    hydrateOnEvents?: (keyof WindowEventMap)[]
    /**
     * The maximum amount of time to wait in milliseconds when waiting for an idle callback. This is useful when there are a lot of network requests happening.
     *
     * @default 8000
     */
    idleCallbackTimeout?: number
    /**
     * Time to wait in milliseconds after the idle callback before hydrating the component.
     *
     * @default 4000
     */
    postIdleTimeout?: number
  }
}

💻 開發

  1. 複製此儲存庫
  2. 使用 corepack enable 啟用 Corepack
  3. 使用 pnpm install 安裝依賴項
  4. 執行 pnpm run dev:prepare
  5. 使用 pnpm run dev 啟動開發伺服器

貢獻者

許可證

MIT 許可證 © 2024-至今 Johann Schopplich