vue性能优化

194 阅读3分钟
  • 将vue2换成vue3
  • Vue 3 在性能优化方面已经做了大量的工作,比如更高效的响应式系统、更小的包体积以及更好的编译优化等。但是,作为开发者,我们仍然可以通过一些实践来进一步提升 Vue 3 应用的性能。以下是一些建议:

合理使用 v-show 和 v-if

  • v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
  • v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
  • 相比之下,v-show 就简单得多——不论初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
  • 因此,如果你需要频繁切换一个元素,使用 v-show 会比 v-if 有更好的性能。
    • v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;
    • v-show 则适用于需要非常频繁切换条件的场景。

v-for避免和 v-if 同时使用

  • 在 v-for 循环中,尽量避免使用复杂的表达式或方法调用,因为它们会在每次渲染循环时被重新计算。相反,将这些逻辑移到计算属性或方法中,并在循环外部调用它们。
  • vue2 v-for优先级高于v-if
  • vue3 v-if优先级高于v-fo

这样写遍历多少次就要判断多少次

<ul> 
    <li v-for="user in users" v-if="user.isActive" :key="user.id" > 
        {{ user.name }} 
    </li> 
</ul>

users 替换为一个计算属性 (比如 activeUsers)

<ul> 
    <li v-for="user in activeUsers" :key="user.id" > 
        {{ user.name }} 
    </li> 
</ul>

 computed 缓存数据

<script setup>
import { ref, computed } from 'vue';

const price = ref(100);
const discount = ref(0.2);

// 计算折扣价(自动缓存)
const discountedPrice = computed(() => {
  console.log('重新计算'); // 依赖变化时才执行
  return price.value * (1 - discount.value);
});
</script>

<template>
  <p>原价: {{ price }} 元</p>
  <p>折扣价: {{ discountedPrice }} 元</p> <!-- 直接使用,无需括号 -->
</template>

v-for 时加 key

将 item 添加到列表前面: 如果不加 key 整个列表都会重新渲染

<template>
  <Item v-for="item in list" :msg="item" :key="item" />
  <button @click="push">push</button>
  <button @click="unshift">unshift</button>
</template>

<script>
import { ref } from "vue";
import Item from "./item";
export default {
  name: "App",
  components: { Item },
  setup() {
    const list = ref([1, 2, 3]);
    // 添加到后面: 加不加key都不影响
    const push = () => list.value.push(Math.random());
    // 添加到前面: 如果不加key 所有的Chid都会重新渲染
    const unshift = () => list.value.unshift(Math.random());

    return { list, push, unshift };
  },
};
</script>

<style>
</style>

异步组件和懒加载

  • 针对体积比较大的组件, 如: 编辑器, 复杂表格, 复杂表单
  • 需要时异步加载, 不需要时不加载
  • 减少主包体积, 首页会加载更快

异步组件

import { defineAsyncComponent } from 'vue';

const AsyncComponent = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
);

路由懒加载


// 通过 `/* webpackChunkName: "自定义名称" */` 注释指定打包后的文件名
const routes = [
  {
    path: '/dashboard',
    component: () => import(/* webpackChunkName: "dashboard" */ '@/views/Dashboard.vue')
  },
  {
    path: '/settings',
    component: () => import(/* webpackChunkName: "user-settings" */ '@/views/Settings.vue')
  }
];

使用 Suspense 和 ErrorBoundary

使用 Suspense 和 ErrorBoundary 处理错误和加载状态

Vue 3 引入了 Suspense 和 ErrorBoundary,允许你更好地处理组件加载和错误状态。你可以使用这些特性来显示加载指示器或错误消息,从而提升用户体验。

Suspense

<Suspense fallback="侧边栏加载中..."> 
    <AsyncSidebar /> <!-- 异步组件 -->
</Suspense>

编写报错组件: ErrorBoundary

<!-- ErrorBoundary.vue -->
<template>
  <div>
    <!-- 捕获错误就降级渲染 -->
    <div v-if="hasError">
      <h2>⚠️ 组件加载出错</h2>
      <p>{{ error?.message }}</p>
      <button @click="resetError">重试</button>
    </div>
    <!-- 正常渲染子组件 -->
    <slot v-else></slot>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const hasError = ref(false);
const error = ref(null);

// 捕获子组件错误
const errorCaptured = (err) => {
  hasError.value = true;
  error.value = err;
  return false; // 阻止错误继续向上传播
};

const resetError = () => {
  hasError.value = false;
  error.value = null;
};

defineExpose({ errorCaptured });
</script>

使用 ErrorBoundary

<!-- ParentComponent.vue -->
<template>
  <ErrorBoundary>
    <CrashComponent /> <!-- 可能抛出错误的组件 -->
  </ErrorBoundary>
</template>

<script setup>
import ErrorBoundary from './ErrorBoundary.vue';
import CrashComponent from './CrashComponent.vue';
</script>

<!-- CrashComponent.vue -->
<script setup>
throw new Error('子组件渲染崩溃!'); // 模拟错误
</script>

keep-alive 缓存组件

  • 频繁切换组件, 如: tabs
  • 不要乱用, 缓存太多会占用内存, 且不好debug

SSR

  • Nuxt.js
  • 成本较高