4.1 响应式原理(Vue3 vs Vue2)⭐
Q17:Vue2响应式原理及缺陷
参考答案:Vue2使用Object.defineProperty对data进行递归遍历,为每个属性添加getter和setter,结合Dep和Watcher实现发布订阅模式。
四大核心缺陷:
- ❌ 无法监听对象的新增/删除属性
- ❌ 无法监听数组下标修改和
length变更
- ❌ 初始化时全量递归,性能损耗大
- ❌ 只能劫持对象已有属性
Vue.set(obj, 'newProp', value);
this.$set(this.obj, 'key', value);
Q18:Vue3响应式原理及Proxy优势 ⭐
标准答案:Vue3使用ES6的Proxy代理整个对象,通过Reflect操作源对象,在get时收集依赖,set/deleteProperty时触发更新。
Proxy优势一览:
| 优势 | 说明 | 对比Vue2 |
|---|
| 监听全属性 | 包括新增/删除属性 | ✅ 完美解决新增问题 |
| 监听数组 | 下标修改、length变更、push/pop等 | ✅ 无需变异方法 |
| 懒代理 | 仅在被访问时才创建代理 | ✅ 性能更优 |
| 13种拦截操作 | has、ownKeys、apply等 | ❌ defineProperty仅能get/set |
Q19:ref vs reactive 的区别与选择 ⭐
| 对比项 | ref | reactive |
|---|
| 适用类型 | 任意类型(基本类型+引用类型) | 仅对象/数组(引用类型) |
| 访问方式 | .value(模板中自动解包) | 直接访问属性,无需.value |
| 替换对象 | 可以整体赋值 count.value = newObj | 不能整体赋值(会破坏响应式),需用Object.assign |
| 传参解构 | 可以解构,保持响应式 | 解构会失去响应式,需用toRefs |
const count = ref(0);
const form = reactive({ name: '', age: 18 });
const user = ref(null);
user.value = { name: 'Tom' };
const { name, age } = toRefs(form);
Q20:computed vs watch vs watchEffect
| 对比 | computed | watch | watchEffect |
|---|
| 返回值 | 返回计算后的值(只读) | ❌ 不返回值 | ❌ 不返回值 |
| 缓存 | ✅ 有,依赖不变不重新计算 | ❌ 无 | ❌ 无 |
| 执行时机 | 访问时触发 | 指定源变化时触发 | 立即执行,自动追踪依赖 |
| 适用场景 | 派生数据(全名、总价) | 异步操作、新旧值对比 | 发送请求、打印日志 |
import { computed, watch, watchEffect } from 'vue';
const fullName = computed(() => `${firstName.value} ${lastName.value}`);
watch(source, (newVal, oldVal) => { }, { deep: true, immediate: true });
watchEffect(() => { console.log(`Count is ${count.value}`); });
4.2 组合式API(Composition API)⭐
Q21:Composition API 相比 Options API 的优势
| 优势 | 说明 | 对比 |
|---|
| 逻辑复用 | 自定义Hook可以跨组件复用有状态的逻辑 | Options API只能用mixins(命名冲突、来源不明) |
| 代码组织 | 按功能关注点聚合代码 | Options API按类型分散(data、methods、watch等) |
| TypeScript支持 | 天然支持类型推导 | 较差(this类型推断困难) |
| 代码体积 | 更好的Tree Shaking | 无法摇树 |
Q22:setup 语法糖的优势
<script setup lang="ts">
const count = ref(0);
interface Props { title: string; count?: number; }
const props = withDefaults(defineProps<Props>(), { count: 0 });
const emit = defineEmits<{ (e: 'update', value: string): void }>();
defineExpose({ reset: () => count.value = 0 });
</script>
Q23:Vue3生命周期 vs Vue2
| Vue2 | Vue3(Options API) | Vue3(Composition API) | 执行时机 |
|---|
beforeCreate | beforeCreate | setup() | 实例初始化,无法访问this |
created | created | setup() | 完成数据观测,可访问data |
beforeMount | beforeMount | onBeforeMount | 挂载开始前 |
mounted | mounted | onMounted | 实例已挂载,可访问DOM |
beforeUpdate | beforeUpdate | onBeforeUpdate | 数据更新,DOM未更新 |
updated | updated | onUpdated | DOM已更新 |
beforeDestroy | beforeUnmount | onBeforeUnmount | 实例销毁前 |
destroyed | unmounted | onUnmounted | 实例已销毁 |
4.3 Diff算法优化
Q24:Vue3 Diff 算法相比 Vue2 的优化
| 优化点 | Vue2(双端Diff) | Vue3(快速Diff) |
|---|
| 静态标记 | ❌ 无,全量比对 | ✅ PatchFlags标记动态内容 |
| 最长递增子序列 | ❌ 直接操作DOM移动 | ✅ 最长递增子序列减少移动次数 |
| 静态提升 | ❌ 无 | ✅ 静态节点提升到渲染函数外 |
| 事件缓存 | ❌ 每次渲染创建新函数 | ✅ 缓存事件处理函数 |
4.4 Pinia 状态管理(Vuex替代方案)
Q25:Pinia vs Vuex 的核心区别 ⭐
| 对比项 | Pinia | Vuex 4.x |
|---|
| 设计理念 | Composition API 风格 | Options API 风格 |
| TypeScript | 天然支持,类型推导完美 | 需要额外类型声明 |
| Mutations | ❌ 没有,直接在actions修改 | ✅ 有mutations |
| 模块化 | 自动模块化(每个store独立) | 需要配置modules |
| 代码量 | 更少、更简洁 | 较多(state/getters/mutations/actions) |
| 生态成熟度 | Vue官方推荐(Vue 3 首选) | Vue 2 时代主流 |
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({ name: '', token: null }),
getters: { isLogin: (state) => !!state.token },
actions: {
async login(username: string, password: string) {
const res = await api.login({ username, password });
this.token = res.token;
this.name = res.name;
}
}
});
const userStore = useUserStore();
const { name, isLogin } = storeToRefs(userStore);
userStore.login('admin', '123456');