vue性能优化

代码优化

静态属性

使用h​函数创建静态节点,这些节点在更新时不会重新渲染,从而提高性能。

import { h } from 'vue';

const App = {
  render() {
    return h('div', null, 'Hello, Vue3!');
  }
};

避免不必要的响应式数据

避免将不需要响应式的数据设置为响应式,可以减少不必要的监听和更新。

import { ref } from 'vue';

const count = ref(0);
const name = 'Vue3'; // 非响应式数据

1">使用虚拟列表1

当需要渲染大量数据时,使用虚拟列表可以减少页面渲染的时间,提高性能。

<virtual-list :data="list" :item-height="50" :render-item="renderItem" />

懒加载

组件懒加载

使用defineAsyncComponent​来实现组件的懒加载,当组件需要时再进行加载,提高页面加载速度。

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

路由懒加载

使用Vue Router的动态导入(Dynamic Imports)​功能,可以实现路由级别的代码拆分。这样,只有当用户访问特定路由时,对应的组件才会被加载。

import { createRouter, createWebHistory } from 'vue-router';

const routes = [
  { path: '/home', component: () => import('./views/Home.vue') },
  { path: '/about', component: () => import('./views/About.vue') }
  // ...其他路由
];

const router = createRouter({
  history: createWebHistory(),
  routes
});

export default router;

使用计算属性缓存

减少watchEffect​的使用,更多使用计算属性,可以减少计算量。

import { ref, computed } from 'vue';

const count = ref(0);
const doubleCount = computed(() => {
  return count.value * 2;
});

避免不必要的渲染

通过使用v-once​指令让组件只渲染一次,或使用defineComponent​创建纯组件来避免这一问题。

<template>
<MyComponent v-if="showComponent" v-once />
<button @click="toggleComponent">切换组件</button>
</template>

<script>
import { defineComponent } from 'vue';
import MyComponent from './MyComponent.vue';

export default defineComponent({
  components: { MyComponent },
  data() {
    return { showComponent: true };
  },
  methods: {
    toggleComponent() {
      this.showComponent = !this.showComponent; // 切换组件渲染
  }
}
});
</script>

使用keep-alive缓存组件

使用keep-alive​可以缓存不活跃的组件实例,避免重复渲染。

<template>
<keep-alive>
<router-view />
</keep-alive>
</template>

代码拆分

懒加载可以实现路由或组件的代码拆分,前文已经叙述过,这里不再赘述。

使用Webpack的SplitChunksPlugin

如果你使用Webpack作为模块打包器,可以利用其内置的SplitChunksPlugin​来自动拆分代码。这个插件可以根据一定的规则将公共代码和第三方库提取到单独的文件中。

// webpack.config.js
module.exports = {
  // ...其他配置
  optimization: {
  splitChunks: {
    chunks: 'all',
    // ...其他配置
    }
  }
};

动态导入第三方库

对于一些不是在应用启动时就需要的第三方库,可以使用动态导入的方式,在需要的时候再加载它们。

// 按需加载lodash库
const _ = await import('lodash');
// 动态导入视频播放器组件
const VideoPlayer = () => import('./components/VideoPlayer.vue');

使用v-if​控制组件的加载

在模板中使用v-if​指令可以根据条件来决定是否渲染某个组件,这样可以在需要的时候才加载对应的组件代码。

<template>
<button @click="showComponent = !showComponent">Toggle Component</button>
<MyComponent v-if="showComponent" />
</template>

<script>
export default {
  data() {
    return { showComponent: false };
  }
};
</script>


  1. 有手就行!轻松打造Vue3高性能虚拟列表组件

    前言

    在前端开发中,我们经常遇到需要展示大量数据列表的场景。如果直接渲染所有列表项,会导致DOM节点过多,影响性能。Vue3虚拟列表通过只渲染用户可视区域内的列表项,极大地提高了渲染效率。本文将详细介绍Vue3虚拟列表的技术原理、实现方式,并提供完整的代码示例和demo。

    引言

    虚拟列表是一种优化大量数据渲染的技术,它通过仅渲染用户可视区域内的列表项,显著减少DOM节点数量,从而提高渲染性能。在Vue.js项目中,虚拟列表尤其适用于长列表数据展示,如商品列表、聊天记录等。

    正文

    虚拟列表的技术原理

    虚拟列表的核心思想是“按需渲染”,即只渲染当前可视区域内的列表项。当用户滚动时,虚拟列表动态地加载和卸载视口内的元素,以降低DOM节点的数量,提升渲染性能。其基本原理包括:

    1. 确定窗口大小:根据屏幕大小、滚动位置等因素动态调整每次渲染的列表项数量。
    2. 计算偏移量:根据当前滚动位置和窗口大小,计算出当前可视区域内的偏移量。
    3. 生成虚拟节点:根据偏移量从数据源中提取相应的列表项,并生成虚拟节点。虚拟节点是轻量级的DOM元素,不会真正插入到DOM树中,而是用于渲染列表项的外观。
    4. 渲染虚拟节点:将虚拟节点按照指定的布局方式渲染到视口中。由于虚拟节点是轻量级的,因此可以快速地创建和销毁它们,从而实现高效的渲染。

    虚拟列表的实现方式

    我们将创建一个简单的虚拟列表组件,该组件使用Vue3的Composition API,并支持渲染用户动态指定的组件。以下是详细的实现步骤和代码示例:

    组件代码
    <template>
      <div class="virtual-list" ref="listContainer" @scroll="onScroll">
        <div class="virtual-list-content" :style="{ height: totalHeight + 'px' }">
          <div
            v-for="(item, index) in visibleItems"
            :key="item.id"
            :style="{ height: itemHeight + 'px', transform: `translateY(${offsetY}px)` }"
            class="virtual-list-item"
          >
            <slot :item="item" :index="index"></slot>
          </div>
        </div>
      </div>
    </template>
    
    <script setup> import { ref, computed, onMounted, onUnmounted } from 'vue';
    
    const props = defineProps({
      items: {
        type: Array,
        required: true,
      },
      itemHeight: {
        type: Number,
        default: 30,
      },
      itemComponent: {
        type: Object,
        required: true,
      },
    });
    
    const listContainer = ref(null);
    const startIndex = ref(0);
    const visibleCount = ref(0);
    const offsetY = ref(0);
    const totalHeight = computed(() => props.items.length * props.itemHeight);
    
    const calculateVisibleCount = () => {
      const container = listContainer.value;
      if (container) {
        const containerHeight = container.clientHeight;
        visibleCount.value = Math.ceil(containerHeight / props.itemHeight) + 1;
      }
    };
    
    const visibleItems = computed(() => {
      const endIndex = startIndex.value + visibleCount.value;
      return props.items.slice(startIndex.value, endIndex);
    });
    
    const onScroll = () => {
      const container = listContainer.value;
      if (container) {
        const scrollTop = container.scrollTop;
        startIndex.value = Math.floor(scrollTop / props.itemHeight);
        offsetY.value = scrollTop - (scrollTop % props.itemHeight);
      }
    };
    
    onMounted(() => {
      calculateVisibleCount();
      window.addEventListener('resize', calculateVisibleCount);
    });
    
    onUnmounted(() => {
      window.removeEventListener('resize', calculateVisibleCount);
    }); </script>
    
    <style scoped> .virtual-list {
      overflow-y: auto;
      height: 400px; 
      border: 1px solid #ddd;
      position: relative;
    }
    
    .virtual-list-content {
      position: relative;
    }
    
    .virtual-list-item {
      box-sizing: border-box;
      border-bottom: 1px solid #f0f0f0;
      line-height: 30px; 
      padding: 0 10px;
    } </style>
    
    使用示例
    <template>
      <div id="app">
        <h1>虚拟列表示例</h1>
        <VirtualList :items="items" :itemHeight="30" :itemComponent="ItemComponent">
          <template #default="{ item, index }">
            <div class="custom-item">{{ item.name }} - {{ index }}</div>
          </template>
        </VirtualList>
      </div>
    </template>
    
    <script setup> import VirtualList from './VirtualList.vue';
    
    const items = Array.from({ length: 10000 }, (v, k) => ({
      id: k,
      name: `Item ${k + 1}`,
    }));
    
    const ItemComponent = {
      template: '<div class="custom-item"><slot></slot></div>',
    }; </script>
    
    <style> #app {
      font-family: Avenir, Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
      margin-top: 60px;
    }
    .custom-item {
      line-height: 30px; 
      padding: 0 10px;
    } </style>
    

    虚拟列表的应用场景和优势

    虚拟列表在实际项目中有着广泛的应用场景,如商品列表、聊天记录、新闻列表等。它的主要优势包括:

    1. 提高渲染性能:通过仅渲染用户可视区域内的列表项,显著减少DOM节点数量,提高渲染性能。
    2. 优化内存使用:减少DOM节点数量,从而降低内存使用。
    3. 支持大数据量:能够高效渲染大量数据,提升用户体验。

    挑战与解决方案

    在使用虚拟列表时,可能会遇到一些挑战,如滚动卡顿、数据更新不同步等。以下是一些解决方案:

    1. 节流/防抖:在滚动过程中,频繁地计算可见项目可能会造成性能下降,可以引入节流或防抖机制。
    2. 异步数据加载:对于数据量特别大的场景,可以采用异步数据加载的方式,逐步加载和渲染数据。

    结论

    虚拟列表是一种优化大量数据渲染的有效技术,它通过仅渲染用户可视区域内的列表项,显著提高了渲染性能。在Vue.js项目中,虚拟列表尤其适用于长列表数据展示。通过本文的介绍和代码示例,希望读者能够掌握Vue3虚拟列表的实现方式,并在实际项目中灵活应用。未来,随着前端技术的不断发展,虚拟列表的实现方式和性能优化也将不断进步,期待更多创新的应用场景和解决方案的出现。