翻译:如何优化 Vue 应用性能

翻译:如何优化 Vue 应用性能

来源文章How To Optimize Performance In Vue Apps

此文作者是 Nuxt.js 核心团队的 Jakub Andrzejewski

译者注

这篇文章发布在 debugbear 上。工具我们可以不用,但是优化的思路可以借鉴一番🤣

其中有许多链接也指向 debugbear,后续可能会把这一系列文章全部翻译为中文。


页面加载性能与更新性能

当谈论在 Vue 应用中优化性能时,有两个主要方面应该被解释:

  • 页面加载性能:这主要关于应用首次访问时内容加载速度以及变得可用的快慢。通常使用核心 Web 指标最大内容渲染(LCP)来衡量。
  • 更新性能:这关注应用对用户操作的响应速度,例如,当有人输入搜索框时列表更新有多快。通常使用核心 Web 指标交互到下一次绘制(INP)来衡量。

理想情况下,我们希望最大化两者,但不同的前端架构使得在每个领域达到性能目标更容易或更难。此外,你正在构建的应用类型对哪些性能方面应该是优先级有很大影响。

本文将主要关注您可以采取哪些措施来提高 Vue 应用性能,例如内置指令、插件和概念。我们不会涉及其他优化方面,如 HTML、CSS、API 改进。

性能测量工具

为了提高性能,我们首先需要一种衡量它的方法。幸运的是,有一些出色的工具允许您在本地开发、持续集成以及生产环境中审计/衡量 Vue 网站的性能。

用于在生产中检查负载性能:

在本地开发期间测试性能可以通过以下方式完成:

最后,您还可以使用以下方式在持续集成中衡量性能:

提升页面加载速度

这是关于应用首次访问时加载内容和变得可用的速度。

构建

如果快速页面加载时间对您的应用至关重要,请尽量避免将其做成纯客户端的 SPA。

服务器发送已包含用户所需内容的 HTML 更好,因为仅客户端渲染会减慢内容可见的时间。服务器端渲染(SSR)或静态站点生成(SSG)可以解决这个问题。要了解更多关于 SSR 如何提高 Vue 应用程序性能的信息,请查看这篇文章

如果你的应用不需要很多复杂的交互性,你也可以使用传统的后端服务器来渲染 HTML,并添加 Vue 进行客户端增强。

如果您需要主应用是一个单页应用(SPA),但又有营销页面(如着陆页或博客页面),考虑将它们分开!理想情况下,这些营销页面应以静态 HTML 的形式部署,使用静态站点生成器,并尽可能减少 JavaScript 的使用。

Bundling 捆绑包

我们向客户端/浏览器发送的代码越少,我们的 Vue 应用程序的性能就越好。

  • 优先选择提供 ES 模块格式且对 tree-shaking 友好的依赖项。
  • 确保删除任何未使用的代码。许多 Vue 插件和集成默认都带有这种设置。
  • 如果您主要使用 Vue 进行渐进增强并希望避免构建步骤,请考虑使用 petite-vue(仅 6kb)。
  • 检查依赖项的大小并评估它提供的功能是否值得。可以使用如 bundlejs.com 之类的工具进行快速检查,但使用您实际的构建设置进行测量始终是最准确的。

代码拆分

代码拆分是构建工具采用的一种技术,用于将应用程序包拆分为更小、更易于管理的块。这些块可以按需或并行加载,通过减少初始需要加载的代码量来优化性能。

可以通过使用以下一种或所有方法来实现。

belowTheFold.jsjs
function lazyLoadImport() {
    return import("./belowTheFold.js");
}

belowTheFold.js 及其依赖将被拆分为单独的块,并且仅在调用 lazyLoadImport() 时才加载。

懒加载对于初始页面加载后不是立即必需的功能特别有用。在 Vue 应用中,可以通过使用 Vue 的异步组件功能来实现,这使您能够将组件树拆分为更小的块,按需加载。

ts
import { defineAsyncComponent } from "vue";

const MyComponent = defineAsyncComponent(() => import("./MyComponent.vue"));

对于使用 Vue Router 的应用,强烈建议使用懒加载路由:

ts
const router = createRouter({
    // ...
    routes: [
        {
            path: "/tasks/:id",
            component: () => import("./views/TaskDetails.vue")
        },
    ],
});

Vue Router 仅在首次进入页面时获取组件代码,然后使用缓存版本。

提升应用响应性

这关注的是应用对用户操作的响应速度,例如,当有人输入搜索框时列表更新有多快。

v-show 和 v-if

v-ifv-show 都控制元素的可见性,但它们以不同的方式做到这一点。 v-if 完全从 DOM 中添加或删除元素,这可能会更消耗性能。另一方面, v-show 切换 display CSS 属性,使其成为需要频繁显示或隐藏的元素的一个更高效的选择。

考虑使用 v-show 来表示频繁切换以使状态变化更高效的元素。

无需后续更新的内容渲染

v-once 是一个内置指令,可用于渲染依赖于运行时数据但无需更新的内容。它所使用的整个子树在所有未来的更新中都将被跳过。

vue
<!-- 单个元素 -->
<span v-once>
This will never change: {{msg}}
</span>

<!-- 包含子元素 -->
<div v-once>
  <h1>Comment</h1>
  <p>{{msg}}</p>
</div>

<!-- `v-for` 指令 -->
<ul>
  <li v-for="i in list" v-once>{{i}}</li>
</ul>

有条件的跳过大型列表更新

v-memo 是一个内置指令,可用于有条件地跳过大型子树或 v-for 列表的更新。

vue
<div v-memo="[valueA, valueB]">
...
</div>

当组件重新渲染时,如果 valueA 和 valueB 保持不变,则将跳过此 <div> 及其子组件的所有更新。

防抖

在管理用户输入,如搜索查询或表单提交时,重要的是要防抖或节流事件以防止性能问题。

防抖延迟函数执行,直到自上次调用以来经过指定时间,而节流确保函数在给定时间间隔内只执行一次。

在这个例子中,搜索功能将在用户停止输入后仅执行 400 毫秒,以最小化 API 调用次数并提高性能。

总体改进

除了页面加载时间和应用响应性之外,还有几个一般性的改进,你可以添加到你的应用中,使其整体性能更优。

避免将静态数据变成响应式

Vue 响应性是一个强大的工具,它允许我们跟踪属性的变化并在引用时修改其值。但有时,我们可能会错误地将静态属性变为响应性,如下面的示例所示:

vue
<script setup lang="ts">
import { ref } from "vue";

const milisecondsInAnHour = ref(3600000);
</script>

milisecondsInAnHour 变量的值不会随时间改变,因此没有必要使其变为响应式。

数据量很大时使用浅反应性

Vue 的响应式系统默认是深层次的,这使得状态管理直观,但处理大数据集时可能会引入开销。这是因为每个属性访问都会触发代理陷阱以进行依赖跟踪。

为了减轻这种情况,Vue 提供了 shallowRef()shallowReactive() ,允许您选择退出深度响应性。这些浅层 API 仅在根级别创建响应式状态,而不会影响嵌套对象。

ts
const shallowArray = shallowRef([
    /* big list of deep objects */
]);

// this won't trigger updates...
shallowArray.value.push(newObject);
// this does:
shallowArray.value = [...shallowArray.value, newObject];

缓存

导航前后通常会导致触发所有渲染视图和组件的逻辑。这可能导致触发对已应经可用的数据的多次请求,因为我们已经访问过这个页面。

对于此类内容,我们可以利用缓存的概念(特别是“陈旧-验证更新”方法),这将允许我们缓存请求的结果并立即将其返回给用户,从而大大提高后续条目的性能。

陈旧-验证策略(Stale-while-revalidate)是一种 HTTP 缓存策略,其中浏览器检查缓存的响应是否仍然新鲜。如果是,浏览器将提供缓存的 内容;如果不是,它将通过从网络获取新鲜响应的同时向用户提供服务陈旧内容来“重新验证”。

在 Vue 中,我们可以像以下这样使用 SWRV 库:

ts
import useSWRV from "swrv";

const { data, error } = useSWRV("/api/user", fetcher);

在这个例子中,Vue 组合式 useSWRV 接受一个 key 和一个 fetcher 函数。 key 是请求的唯一标识符,通常是 API 的 URL。 fetcher 接受 key 作为其参数并异步返回 data 。 useSWRV 也返回 2 个值: data 和 error 。当请求(fetcher)尚未完成时,数据将是 undefined 。

译者注

我们也可以使用 tanstack query 来作为请求的缓存层

渲染长列表

前端应用中最常见的性能挑战之一是渲染大量列表。无论框架多么高效,由于浏览器需要管理的 DOM 节点数量庞大,显示包含数千项的列表可能会很慢。为了解决这个问题,您可以使用 vue-virtual-scroller 包。

避免内存泄露

内存泄露最常见的情况是在组件的初始化添加监听器,但在组件卸载后没有移除它

要记得在组件卸载时移除事件监听器

vue
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'

onMounted(() => window.addEventListener('mousemove', doSomething))
onUnmounted(() => window.removeEventListener('mousemove', doSomething))
</script>

优化图片

通常,未优化的图像是性能不佳的主要原因。然而,通过使用如 unpic/vue 这样的解决方案,这个问题可以轻松解决,unpic/vue 是一个针对 Vue 的高性能响应式图像组件。

vue
<script setup lang="ts">
import { Image } from "@unpic/vue";
</script>

<template>
    <image
        src="https://cdn.shopify.com/static/sample-images/bath_grande_crop_center.jpeg"
        layout="constrained"
        width="800"
        height="600"
        alt="A lovely bath"
    />
</template>

它生成一个响应式的 <img> 标签,遵循最佳实践,具有正确的 srcset、sizes 和样式。该组件还可以检测大多数图像 CDN 和 CMS 中的图像 URL,并且可以在不进行构建步骤的情况下调整图像大小。

摘要

Vue 作为框架,在设计时考虑了性能,同时您还可以使用内置的可选功能,如指令,使其性能更上一层楼。实施前几节中的解决方案可以帮助您优化 Vue 应用的性能。

请记住,为了提高您 Web 应用程序的性能,您还需要优化 HTML、CSS 和 API 的性能,以实现整体性能良好的应用程序。

附加内容:使用 DebugBear 监控 Vue 应用

通过使用 DebugBear,您可以获取关于您 Vue 应用性能的所有有价值数据,关于竞争对手的信息,以及从多个位置运行测试的能力,所有这些都可以在一个仪表板上完成!在此处免费试用!

除了仪表板之外,您还可以使用 HTML 大小分析器来查看哪些内容正在导致您的文档大小。查找如内联图片、大型 hydration 状态或代码重复等问题。

这可以帮助您发现未优化的代码和可以改进的地方。

额外资源

如果您想了解更多关于这些概念的信息,请查看以下文章:

Drizzle ORM 使用
翻译:Vue 检查 slot 是否为空

评论区

评论加载中...