MST

星途 面试题库

面试题:Vue组件性能瓶颈之数据更新触发重渲染问题

在Vue组件中,频繁的数据更新可能导致性能瓶颈。假设你有一个包含大量列表项的组件,当其中一个列表项数据变化时,整个组件都重新渲染,你会如何优化,以避免不必要的重渲染?
24.0万 热度难度
前端开发Vue

知识考点

AI 面试

面试题答案

一键面试
  1. 使用key属性
    • 给列表项添加唯一的key值,Vue 会基于key来判断列表项是否变化,从而复用和更新已有元素,而不是重新创建。例如:
    <template>
      <ul>
        <li v - for="(item, index) in list" :key="item.id">{{item.name}}</li>
      </ul>
    </template>
    <script>
    export default {
      data() {
        return {
          list: [
            {id: 1, name: 'item1'},
            {id: 2, name: 'item2'}
          ]
        };
      }
    };
    </script>
    
  2. 计算属性和侦听器
    • 计算属性:如果组件中有一些依赖于其他数据的派生数据,可以使用计算属性。计算属性会基于它的依赖进行缓存,只有依赖变化时才会重新计算。例如,假设列表项有一个总数计算:
    <template>
      <div>
        <p>Total items: {{ total }}</p>
        <ul>
          <li v - for="(item, index) in list" :key="item.id">{{item.name}}</li>
        </ul>
      </div>
    </template>
    <script>
    export default {
      data() {
        return {
          list: [
            {id: 1, name: 'item1'},
            {id: 2, name: 'item2'}
          ]
        };
      },
      computed: {
        total() {
          return this.list.length;
        }
      }
    };
    </script>
    
    • 侦听器:当需要在数据变化时执行异步操作或开销较大的操作时,可以使用侦听器,并通过deep选项来控制深度监听。例如,监听列表中某个对象的属性变化:
    <template>
      <div>
        <ul>
          <li v - for="(item, index) in list" :key="item.id">
            <input v - model="item.value" />
          </li>
        </ul>
      </div>
    </template>
    <script>
    export default {
      data() {
        return {
          list: [
            {id: 1, value: 'val1'},
            {id: 2, value: 'val2'}
          ]
        };
      },
      watch: {
        list: {
          deep: true,
          handler(newVal, oldVal) {
            // 只有真正数据变化时才执行这里的逻辑,而不是每次都触发
            console.log('list has changed');
          }
        }
      }
    };
    </script>
    
  3. Vue.mixin
    • 可以通过Vue.mixin来提取公共逻辑,减少组件中重复代码,从而可能优化性能。例如,提取列表项数据处理的公共逻辑:
    const myMixin = {
      methods: {
        updateListItem(newData) {
          // 公共的更新列表项逻辑
          const index = this.list.findIndex(item => item.id === newData.id);
          if (index!== -1) {
            this.list[index] = newData;
          }
        }
      }
    };
    Vue.mixin(myMixin);
    
  4. 组件拆分
    • 将大组件拆分成多个小组件,每个小组件只处理自己相关的数据。这样当某个列表项数据变化时,只会触发相关小组件的更新,而不是整个大组件。例如,将列表项单独封装成一个组件:
    <!-- ListItem.vue -->
    <template>
      <li>{{item.name}}</li>
    </template>
    <script>
    export default {
      props: {
        item: {
          type: Object,
          required: true
        }
      }
    };
    </script>
    
    <!-- ParentComponent.vue -->
    <template>
      <ul>
        <ListItem v - for="(item, index) in list" :key="item.id" :item="item" />
      </ul>
    </template>
    <script>
    import ListItem from './ListItem.vue';
    export default {
      components: {
        ListItem
      },
      data() {
        return {
          list: [
            {id: 1, name: 'item1'},
            {id: 2, name: 'item2'}
          ]
        };
      }
    };
    </script>
    
  5. 使用shouldComponentUpdate
    • 在 Vue 2 中,可以通过在组件中定义shouldComponentUpdate方法来控制组件是否需要更新。例如:
    <template>
      <div>
        <ul>
          <li v - for="(item, index) in list" :key="item.id">{{item.name}}</li>
        </ul>
      </div>
    </template>
    <script>
    export default {
      data() {
        return {
          list: [
            {id: 1, name: 'item1'},
            {id: 2, name: 'item2'}
          ]
        };
      },
      shouldComponentUpdate(nextProps, nextState) {
        // 这里可以通过比较当前数据和新数据来决定是否更新
        return this.list!== nextProps.list || this.someOtherData!== nextState.someOtherData;
      }
    };
    </script>
    
    • 在 Vue 3 中,shouldComponentUpdate被移除,取而代之的是使用watchEffectcomputed等更细粒度的响应式控制来达到类似效果。例如,使用watchEffect来监听特定数据变化:
    <template>
      <div>
        <ul>
          <li v - for="(item, index) in list" :key="item.id">{{item.name}}</li>
        </ul>
      </div>
    </template>
    <script setup>
    import {ref, watchEffect} from 'vue';
    const list = ref([
      {id: 1, name: 'item1'},
      {id: 2, name: 'item2'}
    ]);
    watchEffect(() => {
      // 这里只对list数据变化做出响应,避免不必要的重渲染
      console.log('list has changed');
    });
    </script>
    
  6. 虚拟 DOM 优化
    • 虽然 Vue 本身已经基于虚拟 DOM 进行了优化,但合理组织数据和结构可以进一步提升虚拟 DOM 的比对效率。例如,避免在列表项中嵌套过多复杂的结构和动态绑定,保持数据结构的简洁,这样虚拟 DOM 在比对变化时可以更快地确定需要更新的部分。