MST
星途 面试题库

面试题:Vue中ref与reactive在复杂业务场景下的最佳实践与性能调优

在一个大型的Vue项目中,涉及到大量数据的交互和动态渲染,部分数据需要频繁更新,部分数据相对稳定。请从性能优化、代码维护等角度出发,阐述如何合理地选择ref与reactive来声明响应式数据,并举例说明在不同业务场景下的最佳实践。同时,分析在这种复杂场景下,可能会遇到哪些与响应式数据相关的性能问题,以及如何解决。
19.2万 热度难度
前端开发Vue

知识考点

AI 面试

面试题答案

一键面试

一、ref 与 reactive 的选择依据

  1. 性能优化角度
    • ref:适用于单个基本数据类型(如字符串、数字、布尔)或需要深度响应式的对象。当访问或修改 ref 数据时,会直接触发依赖更新。如果数据结构简单且更新频率高,使用 ref 可以减少不必要的代理开销。例如,一个计数器:
import { ref } from 'vue';

export default {
    setup() {
        const count = ref(0);
        const increment = () => {
            count.value++;
        };
        return {
            count,
            increment
        };
    }
};
- **reactive**:用于创建深度响应式的对象或数组。Vue 通过 Proxy 对整个对象进行代理,当对象内部属性变化时会触发响应式更新。对于复杂的对象结构且相对稳定的数据,reactive 更合适,因为它的代理机制能有效追踪对象内部的变化。例如,一个用户信息对象:
import { reactive } from 'vue';

export default {
    setup() {
        const user = reactive({
            name: 'John',
            age: 30,
            address: {
                city: 'New York'
            }
        });
        const updateUser = () => {
            user.age++;
        };
        return {
            user,
            updateUser
        };
    }
};
  1. 代码维护角度
    • ref:语法简单,对于单个数据的操作一目了然,在代码中易于查找和理解。当数据逻辑独立且不依赖其他数据时,使用 ref 能使代码结构更清晰。
    • reactive:对于复杂对象和数组,使用 reactive 能保持数据的整体性,在修改和维护对象内部结构时更方便。例如,管理一个商品列表,列表中的每个商品有多个属性,使用 reactive 声明商品列表对象更便于维护。

二、不同业务场景下的最佳实践

  1. 频繁更新的基本数据类型:使用 ref。如实时显示的系统时间:
import { ref, onMounted } from 'vue';

export default {
    setup() {
        const currentTime = ref('');
        onMounted(() => {
            const timer = setInterval(() => {
                currentTime.value = new Date().toLocaleTimeString();
            }, 1000);
            return () => {
                clearInterval(timer);
            };
        });
        return {
            currentTime
        };
    }
};
  1. 复杂且相对稳定的对象数据:使用 reactive。例如,一个电商购物车,包含多个商品项及其属性,购物车结构相对稳定但数据量较大:
import { reactive } from 'vue';

export default {
    setup() {
        const cart = reactive({
            items: [
                { id: 1, name: 'Product 1', price: 10, quantity: 1 },
                { id: 2, name: 'Product 2', price: 20, quantity: 2 }
            ],
            totalPrice: 0
        });
        const updateTotalPrice = () => {
            cart.totalPrice = cart.items.reduce((acc, item) => acc + item.price * item.quantity, 0);
        };
        return {
            cart,
            updateTotalPrice
        };
    }
};

三、可能遇到的性能问题及解决方法

  1. 不必要的重新渲染
    • 问题:当 reactive 对象中的某个属性变化时,可能会导致整个组件重新渲染,即使其他属性未改变,从而影响性能。
    • 解决方法:使用 computed 计算属性,它会缓存依赖数据,只有依赖数据变化时才重新计算。例如,在上述购物车例子中,totalPrice 可以定义为计算属性:
import { reactive, computed } from 'vue';

export default {
    setup() {
        const cart = reactive({
            items: [
                { id: 1, name: 'Product 1', price: 10, quantity: 1 },
                { id: 2, name: 'Product 2', price: 20, quantity: 2 }
            ]
        });
        const totalPrice = computed(() => {
            return cart.items.reduce((acc, item) => acc + item.price * item.quantity, 0);
        });
        return {
            cart,
            totalPrice
        };
    }
};
  1. 深度嵌套对象更新问题
    • 问题:在深度嵌套的 reactive 对象中,直接修改深层属性可能不会触发响应式更新。
    • 解决方法:使用 Vue 提供的 set 方法(Vue 2.x)或通过解构重新赋值(Vue 3.x)。例如,在上述用户信息对象中,如果要更新 address.city
import { reactive } from 'vue';

export default {
    setup() {
        const user = reactive({
            name: 'John',
            age: 30,
            address: {
                city: 'New York'
            }
        });
        const updateCity = () => {
            // Vue 3.x 方式
            user.address = {
               ...user.address,
                city: 'Los Angeles'
            };
        };
        return {
            user,
            updateCity
        };
    }
};
  1. 大量数据渲染性能问题
    • 问题:当有大量数据需要渲染时,即使使用了合理的响应式声明,渲染性能仍可能受影响。
    • 解决方法:采用虚拟滚动技术,如 vue - virtual - scroll - list 插件,只渲染可见区域的数据,减少 DOM 操作和内存占用。