10 个 Vue 3 性能飙升技巧

2025-08-19 15:25:21

阅读:31
标签:vue

10 个 Vue 3 性能飙升技巧

引言

本文将为你带来 10 个经过实战检验的 Vue 3 性能优化技巧!


技巧 #1:用 shallowReactive 代替 reactive —— 节能模式

  • 问题: 你是否曾遇到过这样的情况:仅仅改变了一个小小的对象属性,却导致整个组件重新渲染?这是因为 Vue 3 的 reactive 默认使用深度响应式,这在处理大型数据集时会严重影响性能。
  • 解决方案: 使用 shallowReactive!它只追踪对象顶层的属性变更,就像是为响应式系统开启了“省电模式”。这对于复杂的对象来说是完美的解决方案。
import { shallowReactive } from "vue";

// 使用 shallowReactive 创建一个仅顶层属性为响应式的对象
const userInfo = shallowReactive({
  name: "前端专家",
  address: { city: "", street: "" }, // 嵌套对象
  hobbies: ["编程", "调试"],
});

// ✅ 顶层属性的变更会触发更新
userInfo.name = "Vue 高手";

// ❌ 嵌套对象属性的变更不会触发更新,从而避免了不必要的渲染
userInfo.address.city = "旧金山";
  • 为何如此: 对于重量级的数据结构(如表格数据、API 响应),shallowReactive 能大幅减少不必要的重渲染。这是 Vue 3 优化中 必备 的技巧。

技巧 #2:使用 toRefs 实现 Ref 解构魔法

  • 问题: 厌倦了手动从响应式对象中解构属性吗?(例如 const name = state.name
  • 解决方案: toRefs 能立即将一个响应式对象的每个属性都转换成独立的 ref
import { reactive, toRefs } from "vue";

// 创建一个响应式对象
const user = reactive({ name: "Jane", age: 30 });

// 使用 toRefs 将对象的属性解构为独立的 ref
const { name, age } = toRefs(user);

// ✅ 现在可以像 ref 一样直接修改值,并触发响应式更新
name.value = "Doe";
  • 为何如此: 它能让你的模板代码更简洁(可以直接使用 {{ name }} 而不是 {{ user.name }}),并遵循 DRY(Don’t Repeat Yourself)原则。这是提升 Vue 3 代码优雅性的 关键

技巧 #3:使用 watchEffect 进行智能侦听

  • 问题:watch 需要立即执行回调或侦听多个数据源时的繁琐配置感到烦恼吗?
  • 解决方案: watchEffect 会自动追踪其依赖,并在依赖变更后重新运行回调函数。
import { ref, watchEffect } from "vue";

const count = ref(0);
const double = ref(0);

// watchEffect 会立即执行一次,然后自动追踪其内部用到的响应式依赖(这里是 `count`)
watchEffect(() => {
  // 当 count 的值变化时,这个函数会自动重新执行
  double.value = count.value * 2;
});

// 增加 count 的值,上面的 watchEffect 回调将会被触发
count.value++;
  • 为何如此: 非常适合处理表单联动、派生状态计算和数据缓存等场景

技巧 #4:Suspense —— 让异步加载如丝般顺滑

  • 问题: 加载异步组件时,用户会看到恼人的白屏吗?
  • 解决方案: 使用 <Suspense> 组件优雅地处理加载状态。
<template>
  <Suspense>
    <!-- #default 插槽:当异步组件加载完成时显示 -->
    <template #default>
      <AsyncComponent />
    </template>

    <!-- #fallback 插槽:在异步组件加载期间显示的“加载中”UI -->
    <template #fallback>
      <div>全力加载中...</div>
    </template>
  </Suspense>
</template>

<script setup>
import { defineAsyncComponent } from "vue";

// 使用 defineAsyncComponent 定义一个异步组件
const AsyncComponent = defineAsyncComponent(() => import("./AsyncComponent.vue"));
</script>
  • 为何如此: 在异步操作期间提供无缝的用户体验。这是提升前端用户体验(UX)的 关键

技巧 #5:Teleport —— 随心所欲地渲染!

  • 问题: 需要将模态框(Modal)或菜单(Menu)渲染到组件树之外的 DOM 位置(如 <body>)吗?
  • 解决方案: <Teleport> 可以将内容“传送”到 DOM 中的任何目标节点。
<template>
  <button @click="show = true">打开模态框</button>

  <!-- 使用 Teleport 将内部内容传送到 body 标签下 -->
  <Teleport to="body">
    <div
      v-if="show"
      class="modal"
    >
      <!-- 这个 div 会被渲染在 <body> 的直接子节点中 -->
      这是一个模态框
      <button @click="show = false">关闭</button>
    </div>
  </Teleport>
</template>
  • 为何如此: 完美解决了 z-index 层级问题和 CSS 作用域限制。这是处理 Vue 3 组件布局的 秘密武器

技巧 #6:v-copy 指令 —— 一键复制

  • 问题: 是否在多个地方重复编写 document.execCommand('copy') 的逻辑?
  • 解决方案: 创建一个可复用的自定义指令。
// 在 main.js 或插件文件中定义一个全局自定义指令
app.directive("copy", {
  // 当指令绑定到元素上时触发
  mounted(el, binding) {
    el.addEventListener("click", () => {
      // 创建一个临时的 textarea 用于执行复制操作
      const textarea = document.createElement("textarea");
      // binding.value 是指令绑定的值,即 'Text to copy'
      textarea.value = binding.value;
      document.body.appendChild(textarea);
      textarea.select();
      document.execCommand("copy");
      // 操作完成后移除临时元素
      document.body.removeChild(textarea);
      alert("已复制!");
    });
  },
});
<!-- 在组件中直接使用指令 -->
<button v-copy="'需要复制的文本'">点我复制</button>
  • 为何如此: 用一行代码实现逻辑复用,极大提升开发效率

技巧 #7:Pinia 插件 —— 为状态管理增添超能力

  • 问题: 复杂的 Pinia store 是不是变得越来越混乱?
  • 解决方案: 使用插件来扩展 store 的功能。
// 插件:为所有 store 添加一个 $reset 方法
const resetPlugin = ({ store }) => {
  // $initialState 是我们自定义添加的属性,用于存储初始状态
  const initialState = JSON.parse(JSON.stringify(store.$state));
  store.$reset = () => store.$patch(initialState);
};

// 创建 Pinia 实例并使用插件
const pinia = createPinia();
pinia.use(resetPlugin);

// 在组件中使用
const userStore = useUserStore();
userStore.$reset(); // 调用插件添加的自定义方法!
  • 为何如此: 集中管理跨 store 的通用逻辑,让你的 Vue 3 状态管理水平更上一层楼

技巧 #8:v-memo —— 为列表渲染涡轮增压

  • 问题: 大型列表不必要的重新渲染导致页面卡顿?
  • 解决方案: v-memo 可以根据依赖项缓存 VNode。
<ul>
  <li
    v-for="item in items"
    :key="item.id"
    <!-- v-memo 接收一个依赖数组 -->
    <!-- 只有当 item.id 或 item.status 变化时,这个 li 才会重新渲染 -->
    v-memo="[item.id, item.status]"
  >
    {{ item.name }} - {{ item.status }}
  </li>
</ul>
  • 为何如此: 对表格和大数据集至关重要。这是 Vue 3 性能优化中的 游戏规则改变者

技巧 #9:useIntersectionObserver —— 智能懒加载

  • 问题: 页面因为加载过多图片而变得缓慢?
  • 解决方案: 使用 VueUse 库中的组合式函数(Composable)实现懒加载。
<script setup>
import { ref } from 'vue';
import { useIntersectionObserver } from '@vueuse/core';

// 创建一个 ref 来引用目标元素
const target = ref(null);
// 一个 ref 用于追踪元素是否可见
const isVisible = ref(false);

// 设置 Intersection Observer
useIntersectionObserver(
  target, // 监听的目标元素
  ([{ isIntersecting }]) => {
    // 当元素进入视口时,isIntersecting 会变为 true
    if (isIntersecting) {
      isVisible.value = true;
    }
  },
);
</script>

<template>
  <img
    ref="target"
    <!-- 仅当元素可见时才设置 src 属性,从而实现懒加载 -->
    :src="isVisible ? 'real-image-url.jpg' : 'placeholder.png'"
    alt="懒加载图片"
  >
</template>
  • 为何如此: 优化资源加载,对 SEO 和用户体验都堪称完美

技巧 #10:自定义 Hooks (Composables) —— 终极复用性

  • 问题: 总是在不同组件间复制粘贴表单验证或数据请求的逻辑?
  • 解决方案: 将逻辑封装在组合式函数(Composables)中。
// composables/useFormValidation.js

import { ref } from "vue";

export default function useFormValidation() {
  // 封装表单数据和错误状态
  const email = ref("");
  const errors = ref({});

  // 封装验证逻辑
  const validate = () => {
    errors.value = {};
    if (!email.value) {
      errors.value.email = "此项为必填项";
    } else if (!email.value.includes("@")) {
      errors.value.email = "请输入有效的邮箱地址";
    }
    // 如果错误对象没有属性,则验证通过
    return Object.keys(errors.value).length === 0;
  };

  // 返回响应式状态和方法
  return { email, errors, validate };
}
<!-- 在组件中使用 -->
<script setup>
import useFormValidation from "./composables/useFormValidation";
// 像使用普通 Hook 一样调用,获取所需的状态和方法
const { email, errors, validate } = useFormValidation();
</script>

<template>
  <input
    v-model="email"
    @blur="validate"
  />
  <p v-if="errors.email">{{ errors.email }}</p>
</template>
  • 为何如此: 实现 DRY、可测试、模块化的代码。这是 Vue 3 代码优化的巅峰之作

评论:

    X

    备案号 皖ICP备19021899号-1  技术支持 © 947968273@qq.com