Nuxt 性能

提升 Nuxt 应用性能的最佳实践。

Maschinelle Übersetzung ·0 % geprüft

Nuxt 提供了内置功能来提升你的应用性能,并有助于改善 Core Web Vitals。此外,Nuxt 团队也维护了多个核心模块,用于在特定领域进一步提升性能。本指南概述了优化 Nuxt 应用性能的最佳实践。

内置功能

Nuxt 提供了若干内置功能,帮助你优化网站性能。理解这些功能的工作方式对于实现极快的性能至关重要。

链接

<NuxtLink> 是 Vue Router 的 <RouterLink> 组件和 HTML <a> 标签的即插即用替代品。它可以智能判断链接是内部链接还是外部链接,并根据情况进行渲染,同时应用可用的优化(预取、默认属性等)

<template>
  <NuxtLink to="/about">关于页面</NuxtLink>
</template>

<!-- 使用 Vue Router 和智能预取时将渲染为 -->
<a href="/about">关于页面</a>

Nuxt 会自动包含智能预取。这意味着它会检测链接何时可见(默认情况下),无论是在视口内还是在滚动时,并预取那些页面的 JavaScript,这样在用户点击链接时页面已经准备就绪。

你也可以选择在交互时进行预取:

export default defineNuxtConfig({
  experimental: {
    defaults: {
      nuxtLink: {
        prefetchOn: {
          interaction: true,
          visibility: false,
        },
      },
    },
  },
})
Weitere Informationen findest du unter NuxtLink.

资源提示

为加快客户端导航,Nuxt 会为请求所解析的 JavaScript 和 CSS 分块添加资源提示。<link rel="preload">(或 modulepreload)标记页面自身需要的分块;<link rel="prefetch"> 则标记入口和已渲染组件延迟导入的分块,以便用户之后导航到相关页面时,这些分块已准备就绪。

HTML 一经解析,预加载提示就会生效。预取提示会以 data-href 替代 href 输出,因此浏览器会忽略它们,直到 Nuxt 在应用就绪后将其激活(参见 onNuxtReady)。这样,在页面加载期间它们就不会与页面自身的资源竞争。你可以通过 experimental.deferPrefetchHints 关闭此行为。如果连接启用了流量节省模式,或使用 2G 网络,则不会激活这些提示。由 noScripts 提供的路由没有客户端来激活它们,因此其非脚本预取提示会以 href 输出。

大型应用的预取集合可能会超过页面实际所需的范围。公开页面和登录后的应用通常会共用一个入口分块,因此预渲染的公开页面可能会预取只有登录后应用才会使用的分块,浪费带宽加载访客永远不会用到的分块。

若要阻止预取某个分块,请在 build:manifest 钩子中将其 manifest 条目的 prefetch 设为 false:

nuxt.config.ts
export default defineNuxtConfig({
  hooks: {
    'build:manifest': (manifest) => {
      for (const chunk of Object.values(manifest)) {
        if (chunk.src?.startsWith('pages/app/')) {
          chunk.prefetch = false
        }
      }
    },
  },
})

这会移除源文件位于 pages/app/ 下的所有分块的 rel="prefetch" 提示,但仍将这些分块保留在 bundle 中,并保留 preload 和 modulepreload 提示。请按公开页面不应预取的目录调整 startsWith 前缀。

你也可以在自己的代码中使用 preloadComponents、preloadRouteComponents 和 prefetchComponents 预加载特定组件或路由。

混合渲染

在更复杂的应用中,我们可能需要对应用的渲染方式进行完全控制,以支持某些页面在构建时生成,而另一些页面则应在客户端渲染的场景。

混合渲染允许为每个路由使用路由规则设定不同的缓存策略,并决定服务器应如何响应给定 URL 的新请求:

export default defineNuxtConfig({
  routeRules: {
    '/': {
      prerender: true,
    },
    '/products/**': {
      swr: 3600,
    },
    '/blog': {
      isr: 3600,
    },
    '/admin/**': {
      ssr: false,
    },
  },
})

Nuxt 服务端将自动注册相应的中间件,并使用 Nitro 缓存层将路由包装为带缓存处理程序的路由。

Weitere Informationen findest du unter 混合渲染.

懒加载组件

要动态导入组件(也称为懒加载组件),只需在组件名称前添加 Lazy 前缀即可。如果该组件并非始终需要,这会很有用。

<script setup lang="ts">
const show = ref(false)
</script>

<template>
  <div>
    <h1>山脉</h1>
    <LazyMountainsList v-if="show" />
    <button v-if="!show" @click="show = true">显示列表</button>
  </div>
</template>

通过使用 Lazy 前缀,你可以延迟在合适的时刻加载组件代码,这有助于优化 JavaScript 包的大小。

Weitere Informationen findest du unter 懒加载组件.

延迟水合

并不总是需要在初始加载时对站点的所有组件进行水合(或使其可交互)。使用延迟水合,你可以控制组件何时加载其代码,这可以改善应用的可交互时间(time-to-interactive)。Nuxt 允许你使用延迟水合来控制组件何时变为可交互(在 Nuxt v3.16 中加入)。

<template>
  <div>
    <LazyMyComponent hydrate-on-visible />
  </div>
</template>

为了优化应用,你可能希望将某些组件的水合延迟到它们可见时,或浏览器完成更重要任务之后再进行。

Weitere Informationen findest du unter 延迟水合.

对于客户端交互很少或没有客户端交互需求的内容网站和营销网站,你还可以进一步结合预渲染、noScripts 路由规则、服务器组件和延迟水合,实现几乎零 JavaScript 的交付。

Weitere Informationen findest du unter 以静态内容为主的网站.

获取数据

为避免对相同数据进行两次获取(一次在服务器上,一次在客户端上),Nuxt 提供了 useFetch 和 useAsyncData。它们可以确保在服务器上发起 API 调用时,数据会通过 payload 转发给客户端,而不是再次获取。

Weitere Informationen findest du unter 数据获取.

Nuxt 核心模块

除了 Nuxt 的内置功能外,Nuxt 团队维护的核心模块还能进一步提升性能。这些模块帮助处理图像、自定义字体或第三方脚本等资源。

图像

未经优化的图像会对网站性能产生显著负面影响,特别是对 最大内容绘制(Largest Contentful Paint,LCP) 分数。

在 Nuxt 中我们可以使用 Nuxt Image 模块,它是面向 Nuxt 应用的即插即用图像优化方案。它允许使用内置优化器或你喜欢的图像 CDN 来调整大小和转换图像。

<NuxtImg> 是原生 <img> 标签的即插即用替代品,并提供以下增强功能:

  • 使用内置提供器优化本地和远程图像
  • 将 src 转换为提供器优化的 URL,使用现代格式如 WebP 或 AVIF
  • 根据 width 和 height 自动调整图像大小
  • 在提供 sizes 选项时生成响应式 sizes
  • 支持原生 lazy loading 以及其他 <img> 属性

你网站中的图像通常可以按重要性区分;那些需要在初始加载时优先交付(即 最大内容绘制)的图像,以及可以稍后或在需要时加载的图像。为此,我们可以使用如下优化:

<template>
  <!-- 🚨 需要尽快加载 -->
  <NuxtImg
    src="/hero-banner.jpg"
    format="webp"
    :preload="{ fetchPriority: 'high' }"
    loading="eager"
    width="200"
    height="100"
  />

  <!-- 🐌 可以稍后加载 -->
  <NuxtImg
    src="/facebook-logo.jpg"
    format="webp"
    loading="lazy"
    fetchpriority="low"
    width="200"
    height="100"
  />
</template>
Weitere Informationen findest du unter Nuxt Image.

字体

Nuxt Fonts 会自动优化你的字体(包括自定义字体)并移除外部网络请求,以提升隐私性和性能。

它包含对任意字体文件的内置自动自托管,这意味着你可以以更优的方式加载网页字体并减少布局偏移,得益于底层包 fontaine。

Nuxt Fonts 会处理你的所有 CSS,并在遇到 font-family 声明时自动执行以下操作:

  1. 解析字体 – 在 public/ 中寻找字体文件,然后检查 Google、Bunny、Fontshare 等网络提供者。
  2. 生成 @font-face 规则 – 注入用于从正确来源加载字体的 CSS 规则。
  3. 代理与缓存字体 – 将 URL 重写为 /_fonts,下载并在本地缓存字体。
  4. 创建回退度量 – 调整本地系统字体以匹配网络字体,减少布局偏移(累积布局偏移(CLS))。
  5. 将字体包含在构建中 – 将字体与项目打包,哈希文件名并设置长期缓存头。

它支持多种提供者,设计为可插拔和可扩展,因此无论你的设置如何,都应该能够使用现有提供者或编写你自己的提供者。

脚本

第三方资源如分析工具、视频嵌入、地图和社交媒体集成可以增强网站功能,但也可能显著降低用户体验,并对 交互到下一次绘制(Interaction to Next Paint,INP) 和最大内容绘制(LCP)分数产生负面影响。

Nuxt Scripts 让你以更好的性能、隐私、安全性和开发者体验来加载第三方脚本。

Nuxt Scripts 在第三方脚本之上提供了一个抽象层,提供 SSR 支持和类型安全,同时仍然让你对脚本的加载方式拥有底层控制。

const { onLoaded, proxy } = useScriptGoogleAnalytics(
  {
    id: 'G-1234567',
    scriptOptions: {
      trigger: 'manual',
    },
  },
)
// 队列事件,待 ga 加载后发送
proxy.gtag('config', 'UA-123456789-1')
// 或等待直到 ga 加载完成
onLoaded((gtag) => {
  // 脚本已加载
})
Weitere Informationen findest du unter Nuxt Scripts.

分析工具

要提升性能,我们首先需要了解如何衡量性能,从在本地开发环境中测量性能开始,然后对已部署到生产环境的应用进行审计。

Nuxi Analyze

nuxt 的 此命令允许你分析 Nuxt 应用的生产构建包。它利用 vite-bundle-visualizer(类似于 webpack-bundle-analyzer)生成应用构建包的可视化表示,让你更容易识别哪些组件占用的空间最大。

当你在可视化中看到一个较大的块时,通常表示存在优化机会——无论是将其拆分为更小的部分、实现懒加载,还是用更高效的替代方案进行替换,尤其是对于第三方库。

包含多个元素的大块通常可以通过只导入必要的组件,而不是整个模块来缩小;大型独立块则更适合通过懒加载来处理,而不是将其包含在主包中。

Nuxt DevTools

通过 Nuxt DevTools,你可以深入了解 Nuxt 应用,发现性能问题并轻松管理应用配置。

它提供了若干可用于衡量 Nuxt 应用性能的功能:

  1. 时间线(Timeline)– 跟踪渲染、更新和初始化组件所花费的时间,以识别性能瓶颈。
  2. 资源(Assets)– 显示未经过转换的文件大小(例如图像)。
  3. 渲染树(Render Tree)– 显示 Vue 组件、脚本和样式之间的连接,以优化动态加载。
  4. 检查(Inspect)– 列出 Vue 应用中使用的所有文件及其大小和评估时间。

Chrome 开发者工具

Chrome 开发者工具提供两个用于衡量性能的有用选项卡:Performance 和 Lighthouse。

打开 Performance 面板时,它会立即显示本地的 最大内容绘制(Largest Contentful Paint,LCP) 和 累积布局偏移(Cumulative Layout Shift,CLS) 分数(良好、需要改进或糟糕)。

如果你与页面进行交互,它还会捕获 交互到下一次绘制(Interaction to Next Paint,INP),并根据你的设备和网络情况提供一套完整的核心网页指标(Core Web Vitals)视图。

Lighthouse 会对性能、无障碍、SEO、渐进式 Web 应用和最佳实践进行审计。它会对你的页面运行测试并生成报告。将未通过的审计作为改进网站的指南。

每项审计都有参考文档,解释该审计为何重要以及如何修复问题。

PageSpeed Insights

PageSpeed Insights(PSI) 报告页面在移动设备和桌面设备上的用户体验,并提供改进该页面的建议。

它提供页面的实验室数据和现场数据。实验室数据在受控环境中收集,适合调试问题;现场数据用于捕获真实世界中的用户体验。

WebPageTest

WebPageTest 是一个网页性能工具,提供有关页面在各种条件下表现的深度诊断信息。

每项测试都可以从世界各地的不同位置运行,在真实浏览器上执行,并可覆盖任意数量的可定制网络条件。

常见问题

在构建更复杂的 Nuxt 应用时,你可能会遇到下面列出的一些问题。理解这些问题并修复它们将有助于提升你网站的性能。

过度使用插件

问题:大量插件可能导致性能问题,尤其是当它们需要昂贵的计算或初始化耗时较长时。由于插件在水合阶段运行,低效的设置会阻塞渲染并降低用户体验。

解决方案:检查你的插件,看看是否有些可以改为实现为 composable 或工具函数。

未使用的代码 / 依赖

问题:随着项目的发展,可能会出现一些未使用的代码或依赖。这些额外的功能如果不再使用或不需要,会增加项目的包体积。

解决方案:检查你的 package.json 以查找未使用的依赖,并分析代码中未使用的工具 / composable / 函数。

未使用 Vue 性能建议

问题:Vue 文档 列出了一些我们也可以在 Nuxt 项目中使用的性能改进方法,但由于它们属于 Vue 文档的一部分,开发者往往会忘记并只关注 Nuxt 特定的改进——然而 Nuxt 应用本质上仍然是一个 Vue 项目。

解决方案:使用诸如 shallowRef、v-memo、v-once 等概念来提升性能。

未遵循规范

问题:项目中越多的人在同时工作,维护稳定代码库就越困难。开发者倾向于在项目中引入他们在其他项目中见过的新概念,这可能导致冲突并影响性能。

解决方案:在项目中建立规则和模式,例如 Vue Composables 的良好实践和设计模式

试图同时加载所有内容

问题:当页面加载时,如果没有正确指示元素的加载顺序,可能会导致同时获取所有资源——这会很慢并导致糟糕的用户体验。

解决方案:使用渐进增强(Progressive Enhancement)等概念,先加载核心网页内容,然后在浏览器/网络条件允许的情况下逐步添加更细致和更复杂的表现层和功能。

有用资源

要了解更多提升性能的各种技术,请查看以下资源:

  1. 使用 PRPL 模式实现即时加载
  2. 感知性能(Perceived performance)
  3. 理解关键渲染路径(Critical Rendering Path)

Was this helpful?