面试题答案
一键面试可能出现的问题及分析
性能瓶颈
- 响应式数据过多:Vue Composition API 中,大量使用
reactive
或ref
创建响应式数据,会导致依赖追踪和更新开销增大。例如在一个大型列表组件中,为每个列表项创建过多的响应式数据,当数据变化时,Vue 需要遍历大量依赖进行更新,影响性能。 - 函数频繁调用:在
setup
函数中定义的函数可能会在模板中频繁调用,每次调用都可能触发不必要的计算,如一些复杂的计算函数。 - 组件销毁时未清理:如果在
setup
中使用了定时器、事件监听等,在组件销毁时未正确清理,会导致内存泄漏,随着组件的频繁创建和销毁,性能会逐渐下降。
命名冲突
- 局部变量冲突:不同的
setup
函数中可能定义相同名称的局部变量。例如多个组件都在setup
中定义了名为count
的变量,当这些组件在同一页面使用时,可能会导致命名冲突。 - 函数名冲突:多个
setup
中定义相同名称的函数。比如都定义了名为handleClick
的函数,在复用逻辑时可能会产生混淆。
优化和解决方案
针对性能瓶颈
- 优化响应式数据
- 减少响应式数据粒度:只对真正需要响应式的部分数据使用
reactive
或ref
。例如在列表组件中,对于一些静态数据,无需设置为响应式。 - 使用
computed
缓存计算结果:对于复杂计算,使用computed
进行缓存。例如:
- 减少响应式数据粒度:只对真正需要响应式的部分数据使用
import { ref, computed } from 'vue';
const count = ref(0);
const doubleCount = computed(() => count.value * 2);
- 避免函数频繁调用
- 将复杂计算逻辑提取到
computed
:把模板中频繁调用的函数逻辑改为computed
。如在模板中频繁调用一个计算总价的函数calculateTotal
,可以改为computed
。
- 将复杂计算逻辑提取到
import { ref, computed } from 'vue';
const prices = ref([10, 20, 30]);
const totalPrice = computed(() => prices.value.reduce((acc, price) => acc + price, 0));
- 正确清理副作用
- 使用
onBeforeUnmount
钩子:在组件销毁前清理定时器、事件监听等。例如:
- 使用
import { onBeforeUnmount, ref } from 'vue';
const timer = ref<number>();
const startTimer = () => {
timer.value = setInterval(() => {
console.log('Timer is running');
}, 1000);
};
onBeforeUnmount(() => {
if (timer.value) {
clearInterval(timer.value);
}
});
针对命名冲突
- 使用命名空间
- 封装逻辑为函数并添加前缀:将复用逻辑封装成函数,函数名添加特定前缀。例如:
// user-utils.ts
export const userGetInfo = () => {
// 获取用户信息逻辑
};
export const userUpdateInfo = () => {
// 更新用户信息逻辑
};
- 使用 TypeScript 接口和类型别名
- 通过类型定义避免冲突:使用接口和类型别名明确变量和函数的类型。例如:
interface User {
name: string;
age: number;
}
const getUser = (): User => {
return { name: 'John', age: 30 };
};
实际项目应用案例
性能优化案例
在一个电商项目的商品列表页面,最初每个商品项都有大量响应式数据,导致页面滚动卡顿。通过分析,将一些不影响视图更新的商品属性(如商品描述中的一些固定文本)从响应式数据中移除,同时对商品总价计算使用 computed
进行缓存,优化后页面性能得到显著提升。
解决命名冲突案例
在一个后台管理系统项目中,不同模块的组件都有自己的 setup
函数,为避免命名冲突,将各个模块的复用逻辑封装成以模块名作为前缀的函数,如用户模块的逻辑函数命名为 userGetList
、userUpdateStatus
等,权限模块的函数命名为 permissionCheck
等,有效避免了命名冲突。