Vue 和 Nuxt 的性能检查清单
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
提升 Web 应用性能永远都是热门话题。我们希望页面加载更快、更流畅,并且减少布局切换(Core Web Vitals,说的就是你 😉)。如果你在 Google 上搜索相关关键词,vue/nuxt performance会找到大量文档和文章,可以用来提升页面性能。本文旨在将所有这些知识汇总到一个权威来源中(尊重文章作者的观点)。
本摘要文件基于以下文章:
- @dawntraoz的Nuxt、Storyblok 和 Netlify 网页性能优化之旅
- @jefrydco报道:我们如何实现 DANA 家庭购物 90+ Lighthouse 性能评分和完全离线模式
- 2020 年 7 月, Johannes Lauter 撰写了关于Web Vitals、Google 搜索、State Vue 和 Nuxt 性能优化的文章。
- Vue.js 性能由@filrakowski提供
以及我多年来积累的知识。
请务必阅读这些文章,并给它们和作者们点个赞😊
您还可以查看我最近撰写的另一篇文章,了解如何使用 Lighthouse CI 和 GitHub Actions 持续衡量 Nuxt.js 应用程序的性能,链接在此。
请记住,提升网站性能并非一朝一夕就能解决的问题。这是一个持续的过程,需要定期关注网站性能,以确保网站的新功能(当然是必要的)不会影响其性能。
预加载密钥请求/预连接到所需源
在 HTML 中声明预加载链接,以指示浏览器尽快下载关键资源。
<head>
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="critical.js" as="script">
</head>
考虑添加 preconnect 或 dns-prefetch 资源提示,以便尽早与重要的第三方源建立连接。
<link rel="preconnect" href="https://example.com">
<link rel="dns-prefetch" href="https://example.com">.
dns-prefetch 的工作原理与 preconnect 完全相同,但具有更广泛的浏览器支持。
减少第三方使用
第三方代码可能会显著影响加载性能。但是,您可以通过以下方式修改使用此第三方库的方式:
- 使用 async 或 defer 属性加载脚本,以避免阻塞文档解析。
- 如果第三方服务器速度慢,则自行托管脚本。
- 如果脚本对您的网站没有明显价值,请将其移除。
- 使用 link rel=preconnect 或 link rel=dns-prefetch 对托管第三方脚本的域执行 DNS 查询。
消除渲染阻塞资源
资源占用导致页面首次渲染失败。请考虑将关键的 JS/CSS 内联加载,并将所有非关键的 JS/样式延迟加载。这样,您只需加载所需的代码和样式,即可减小页面大小。
一旦你确定了关键代码,就将该代码从阻塞渲染的 URL 移动到 HTML 页面中的内联脚本标签。
在 HTML 页面头部样式块中内联首次绘制所需的关键样式,并使用预加载链接异步加载其余样式。
您可以在这里阅读更多相关内容。
压缩/移除不必要的 CSS 和 JS
在构建大型应用程序时,你会发现项目的代码量远远超过实际需要和使用的代码量。
使用CSS Minification或Terser JS 插件之类的工具。在 Nuxt 中,Terser 是默认包含的。
要删除未使用的 CSS,请使用PurgeCSS之类的工具。
为了消除不必要的 JavaScript 代码,您可以使用前面提到的 Terser,或者利用Tree Shaking来实现死代码消除。您还可以使用代码分割,将代码拆分成可以按需加载的模块。
Nuxt 提供开箱即用的代码分割功能。
扫描模块以查找重复项。
从软件包中删除大型、重复的 JavaScript 模块,以减小最终软件包的大小。
使用Webpack 捆绑分析器或--analyzeNuxt.js 中的标志
缩短执行时间
代码分割、精简和压缩、删除未使用的代码以及缓存技术的结合将大大提高执行速度。
考虑减少解析、编译和执行 JavaScript 所花费的时间。您可能会发现,提供更小的 JavaScript 有效负载有助于提高效率。
我们的目标是优化 JavaScript 和 CSS 代码,尽可能减少代码量,移除未使用的代码以及我们使用的第三方库。
务必保持主文档的服务器响应时间短,因为所有其他请求都依赖于它。
您可以在这里阅读更多相关内容。
图像处理
调整图片尺寸
提供大小合适的图片,以节省移动数据流量并提高加载速度。
<img src="cat-large.jpg" srcset="cat-small.jpg 480w, cat-large.jpg 1080w" sizes="50vw">
您可以在这里阅读更多相关内容。
高效地对图像进行编码
优化后的图片加载速度更快,消耗的流量也更少。
使用图片 CDN 服务或对图片进行压缩就足够了。
您可以在这里阅读更多相关内容。
以下一代格式提供图像
WebP 或 Avif 等图像格式通常比 PNG 或 JPEG 提供更好的压缩效果,这意味着更快的下载速度和更少的数据消耗。
您可以在这里阅读更多相关内容。
图像元素具有明确的宽度和高度
为图像元素设置明确的宽度和高度,以减少布局偏移并改善CLS。
您可以在这里阅读更多相关内容。
预加载最大内容绘制 (LCP)
预加载 LCP 元素使用的图像,以改善 LCP 时间。
<link rel="preload" href="/path/to/image.jpg" as="image">
head() {
return {
link: [
{
rel: 'preload',
as: 'image',
href: 'path/to/lcp/image',
},
],
}
}
您可以在这里阅读更多相关内容。
字体
网页字体加载期间所有文本均保持可见
利用 font-display CSS 功能,确保在网页字体加载时文本对用户可见。
@font-face {
font-family: 'Arial';
font-display: swap;
}
字体显示 API 指定字体的显示方式。swap 函数告诉浏览器,使用该字体的文本应立即使用系统字体显示。自定义字体准备就绪后,它会替换系统字体。
例如,对于 Google 字体,只需在 Google 字体网址末尾添加 &display=swap 参数即可:
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700&**display=swap**" rel="stylesheet">
您可以在这里阅读更多相关内容。
应该避免什么?
布局大幅调整
累积布局偏移 (CLS) 是 Core Web Vitals 的一项指标,它是通过对所有非用户交互引起的布局偏移进行求和计算得出的。
避免 DOM 大小过大
过大的 DOM 会增加内存使用量,导致样式计算时间延长,并产生代价高昂的布局重排。
多次页面重定向
重定向会增加页面加载前的延迟。
为现代浏览器提供旧版 JavaScript 服务
Polyfill 和转换函数使旧版浏览器能够使用新的 JavaScript 特性。然而,对于现代浏览器来说,其中许多特性并非必需。
在 Nuxt 中,我们在构建命令中使用了 --modern 选项。
巨大的网络有效载荷
大型网络负载会给用户带来实际的经济损失,并且与较长的加载时间高度相关。
- 等到需要时再提交请求。Nuxt 会处理这些请求。
- 尽可能优化请求大小,进行最小化和压缩,并尽可能使用 WebP 格式的图片。我们会始终使用图片 CDN 来保持性能稳定!
- 缓存请求,这样页面在重复访问时就不会重新下载资源。
Document.write()
对于网络连接速度较慢的用户,通过 document.write() 动态注入的外部脚本可能会使页面加载延迟数十秒。
非合成动画
未合成的动画可能会占用大量资源并增加内存占用。请改用translateCSSscale属性。
框架改进
我们已经了解了如何使用 HTML、CSS 和 JavaScript。现在,让我们来看看框架层,看看我们可以做些什么来提升网站性能。
异步组件
异步组件允许您仅在满足特定条件时才加载组件。
<template>
<header>
<Search v-if="searchActive" />
<button @click="searchActive = !searchActive">
🔍
</button>
</header>
</template>
<script>
export default {
components: {
Search: () => import('~/components/search.vue')
},
data() {
return {
searchActive: false
}
}
}
</script>
基于路由的代码分割
只会下载用户当前访问的路由中的代码。
所以,取而代之的是:
// router.js
import Home from './Home.vue'
import About from './About.vue'
const routes = [
{ path: '/', component: Home }
{ path: '/about', component: About }
]
我们可以这样写:
// router.js
const routes = [
{ path: '/', component: () => import('./Home.vue') }
{ path: '/about', component: () => import('./About.vue') }
]
如果您使用的是 Nuxt,那么这项功能是开箱即用的。Nuxt 默认的基于目录的路由系统默认会对每条路由进行代码分割。
仅在真正需要时才使用响应式设计
在页面上堆砌过多的响应式属性会降低页面速度(尤其是在使用 Vue 2 时)。请确保仅在必要时使用它们,并将不会随时间变化的静态值存储在常量变量中。
所以,取而代之的是:
export default {
data() {
return {
milisecondsInAnHour: 3600000,
}
},
computed: {
getMilisecondsInAnHour() {
return this.milisecondsInAnHour
}
}
}
写类似这样的内容:
const MILISECONDS_IN_AN_HOUR = 3600000;
export default {
computed: {
getMilisecondsInAnHour() {
return MILISECONDS_IN_AN_HOUR
}
}
}
消除内存泄漏
内存泄漏最简单的例子是注册了一个事件监听器但没有正确地注销它。
export default {
created() {
target.addEventListener(type, listener);
}
}
为避免这种情况,请确保在销毁生命周期钩子中包含 removeEventListener。
优化第三方套餐
许多流行的第三方软件包都提供了轻量级版本,您可以使用https://bundlephobia.com/进行查看。Bundlephobia 可以帮助您了解 npm 软件包对性能的影响。查找任何 JavaScript 软件包的大小及其对前端打包的影响。
务必使用支持 tree shaking 的库,以便仅加载最终配置中将使用的代码。
有些库(例如 lodash)支持直接导入文件而不是整个库。因此,无需这样写:
import { isNull } from 'lodash'
我们可以使用这个:
import isNull from 'lodash/isNull'
[Nuxt] 仅当插件在应用程序范围内使用时才使用它们。
插件是提供应用程序全局逻辑的绝佳方式,但也意味着它们会被加载到整个应用程序中。如果您只需要在特定条件下或特定页面中使用某些逻辑,请考虑在这些地方通过动态导入来加载它们。
【基础设施】使用内容分发网络(CDN)
CDN 可以快速传输加载互联网内容所需的资源,包括 HTML 页面、JavaScript 文件、样式表、图像和视频。您可以为所有资源配置 CDN
。build.publicPath
实用的 Vue 和 Nuxt 软件包
想要提升网站性能,有很多软件包可供选择。
实现渐进式 Web 应用
PWA会缓存加载应用所需的所有资源。不仅包括JS和CSS等静态文件,还包括图片。甚至API响应也会被缓存。
pwa: {
manifest: {
name: 'My Awesome App',
lang: 'fa',
}
}
预连接字体
如果您正在使用 Google Fonts(如 Roboto、Raleway 等),您可以使用此软件包来避免阻塞页面渲染。
googleFonts: {
families: {
Roboto: true,
Raleway: {
wght: [100, 400],
ital: [100]
},
}
}
使用优化后的图片
确保所有图片尺寸合适,和/或使用 Cloudinary 等外部数字资产管理工具实时优化图片。
image: {
cloudinary: {
baseURL: 'https://res.cloudinary.com/<your-cloud-name>/image/upload/'
}
}
清除不必要的 CSS
PurgeCSS 会分析您的内容和 CSS 文件。然后,它会将 CSS 文件中使用的选择器与内容文件中的选择器进行匹配。它会从 CSS 中移除未使用的选择器,从而减小 CSS 文件的大小。
懒人补水
延迟水合的理念是能够(通过 JavaScript)控制哪些组件被水合,何时以及在什么条件下被水合。
<LazyHydrate when-visible>
<AdSlider/>
</LazyHydrate>
图片延迟加载
图片懒加载的概念是延迟发送图片请求,直到图片出现在视口中才发送。所以简单来说,如果页脚有张图片,那么只有当用户向下滚动页面看到它时,才会发送图片请求。
<img v-lazy="img.src">
无限加载
无限加载的概念是,当用户滚动浏览页面时,我们会加载下一页的数据。
<template>
<infinite-loading></infinite-loading>
</template>
<script>
import InfiniteLoading from 'vue-infinite-loading';
export default {
components: {
InfiniteLoading,
},
};
</script>
使用压缩(Brotli)
添加 Brotli 压缩可以显著减小应用程序的整体文件大小,Alexander Lichter 写了一篇很棒的文章介绍了如何添加它。
module.exports = {
modules: [
['nuxt-compress', {
brotli: {
threshold: 8192,
},
}],
],
};
缓存
缓存是一种硬件或软件组件,用于存储数据,以便更快地响应将来对该数据的请求。
cache: {
useHostPrefix: false,
pages: [
'/page1',
'/page2',
],
store: {
type: 'memory',
max: 100,
ttl: 60,
},
},
奖金
- 请确保
min-height所有主要页面都包含此功能。我们在 Vue Storefront 中遇到了这个问题,因为我们需要从电商平台获取数据,所以内容加载需要一些时间,导致布局偏移(从而导致 Lighthouse 测试结果变差,不过这个问题比较容易解决)。 - 务必检查组件是否存在不必要的导入。开发大型应用程序时,重构页面是很常见的,这意味着需要不断地添加或删除组件。这样很容易忽略哪些组件、辅助函数等已被使用,哪些未被使用。
- 检查你的构建配置
nuxt.config.js。为了减小最终打包文件的大小,你可以extractCSS: true向你的build对象中添加一些参数。在我们的例子中,这样做使最终打包文件的大小减少了大约 60 KB(占总大小的 20%)。

