深色模式
Vue 2/3 对比与迁移
38. Vue 2 与 Vue 3 的核心区别怎么系统回答?
展开答案与追问
参考答案: 按响应式、组织方式、渲染优化、应用 API、类型支持与破坏性变更分层回答。Vue 3 使用 Proxy 与独立响应式能力,强化 Composition API、编译器辅助 patch 与 TypeScript 体验;Vue 2.7 已回移部分语法和 API,不能将所有 Composition API 能力都说成 Vue 3 独占。
追问与易错点: 下面对比表用于复习,真实迁移要按第三方依赖和项目版本逐项核对。迁移指南
39. 为什么 Vue 3 不需要 Vue.set?
展开答案与追问
参考答案: 对 reactive 返回的代理添加或删除属性可以被拦截,通常直接赋值或 delete 即可。关键是操作代理;如果绕到原始对象修改,不会自动经过该代理的拦截。Vue 2 则需要处理已观察对象无法侦测属性新增等限制。
追问与易错点: 不要把“无需 $set”理解成解构基本值、外部原始引用或任意 class 实例都会自动响应。
40. Vue 2 到 Vue 3 的组件 API 有哪些迁移点?
展开答案与追问
参考答案: 组件 v-model 默认由 value/input 改为 modelValue/update:modelValue;.sync 对应的场景转向带参数 v-model。事件声明用 emits,$listeners 合并进 $attrs,.native 被移除,插槽统一为函数。还需处理过滤器、实例事件总线和多根组件透传。
追问与易错点: 迁移不是简单全局替换字符串,要确认组件库契约和自动透传行为。破坏性变更
41. 为什么 createApp 比全局 Vue API 更容易隔离?
展开答案与追问
参考答案: Vue 2 常通过构造器上的全局配置与插件影响实例;Vue 3 使用 createApp 创建应用,app.use、app.component 等配置以应用为边界,利于同页多应用和测试隔离。模块级可变单例依然可能泄漏跨应用或 SSR 请求状态。
追问与易错点: Vue 3 的应用隔离不是对用户代码中所有共享变量自动隔离。
42. 实际项目如何从 Vue 2 迁移到 Vue 3?
展开答案与追问
参考答案: 先盘点 Vue、Router、状态管理、UI 库、构建与测试版本和破坏性 API;建立关键业务回归;评估 @vue/compat 并按警告修复,或按应用边界分阶段切换;最后移除兼容配置、验收性能与行为。
追问与易错点: 不必同时重写全部 Options API。兼容构建有明确限制,不能保证所有 Vue 2 插件直接运行。兼容构建
43. Pinia/Vuex 与普通 reactive 单例有什么区别?
展开答案与追问
参考答案: 普通单例可提供共享响应式数据,但调试、模块契约、插件、测试隔离及 SSR 生命周期需要自行设计。Pinia/Vuex 提供更明确的状态组织与工具支持;选型应结合既有项目,不是数据放入 store 就自动性能更好。
追问与易错点: 解构 Pinia store 的响应式状态时可用 storeToRefs;actions 可直接解构。SSR 要按请求隔离状态。Pinia 核心概念
速查表与代码示例
Vue 2 / Vue 3 对比表
| 维度 | Vue 2 常见机制 | Vue 3 常见机制 | 回答边界 |
|---|---|---|---|
| 对象响应式 | defineProperty 逐属性处理 | Proxy 拦截对象操作,ref 跟踪 value | Vue 2.7 仍沿用 Vue 2 响应式底层 |
| 属性新增/删除 | 需要 set/delete 等辅助方式 | 操作响应式代理可直接捕获 | 绕过代理操作原对象不同 |
| 数组 | 封装变更方法,索引/length 有限制 | 代理并配合数组专门处理 | 原生数组行为还需响应式工具适配 |
| Map/Set | 不具备 Vue 3 同等集合响应支持 | 有集合类型的响应式处理 | 不是任意宿主或内建对象都适用 |
| 逻辑组织 | Options API 为主 | 支持 Options / Composition | Vue 2.7 也有 Composition API |
| 应用入口 | new Vue 与全局配置 | createApp 与应用级配置 | 业务模块单例仍需自行隔离 |
| 根节点 | 模板通常单根 | 支持 Fragment 多根 | 多根时 attrs 透传需明确目标 |
| 组件 v-model | 默认 value / input | 默认 modelValue / update:modelValue | 支持带参数多个 v-model |
| 生命周期 | beforeDestroy / destroyed | beforeUnmount / unmounted | setup 不是 created 简单改名 |
| 渲染优化 | VNode 与运行时 Diff | 编译提示、静态优化、Block 等 | Vue 2 也有编译优化,非完全没有 |
| keyed 列表中间段 | 常见双端比较 | 索引映射及 LIS 减少移动 | 不代表任意输入都必然走相同路径 |
| TS 体验 | 可用,但选项式推断有约束 | 更适配类型推断和组合式逻辑 | 编译类型不替代接口运行时校验 |
| 迁移 API | filters、$on/$off 等旧用法 | 移除部分旧 API,改用函数/显式订阅 | 需核对 UI 库和插件兼容 |
对比依据:Vue 2 响应式说明、Vue 2.7 说明、Vue 3 迁移指南。
生命周期速查
| 阶段/目的 | Vue 2 Options API | Vue 3 Options API | Vue 3 Composition API |
|---|---|---|---|
| 初始化前后 | beforeCreate / created | beforeCreate / created | setup 中组织初始化,无 onCreated |
| 挂载前 | beforeMount | beforeMount | onBeforeMount |
| 挂载后 | mounted | mounted | onMounted |
| 更新前 | beforeUpdate | beforeUpdate | onBeforeUpdate |
| 更新后 | updated | updated | onUpdated |
| 卸载前 | beforeDestroy | beforeUnmount | onBeforeUnmount |
| 卸载后 | destroyed | unmounted | onUnmounted |
| 缓存激活/停用 | activated / deactivated | activated / deactivated | onActivated / onDeactivated |
| 后代错误捕获 | errorCaptured | errorCaptured | onErrorCaptured |
| SSR 数据预取 | serverPrefetch(支持版本) | serverPrefetch | onServerPrefetch |
Vue 3 的 mounted/updated/unmounted 一类客户端钩子不在 SSR 运行;setup/创建阶段需避免无清理机会的浏览器副作用。onErrorCaptured 不替代所有异步错误处理。详细语义见 生命周期 API。
示例:Vue 3 监听参数、清理请求与使用 computed
vue
<script setup>
import { computed, ref, watch } from 'vue';
const props = defineProps({ userId: { type: String, required: true } });
const user = ref(null);
const loading = ref(false);
const error = ref('');
const displayName = computed(() => user.value?.name ?? '暂无用户');
watch(() => props.userId, async (id, _oldId, onCleanup) => {
const controller = new AbortController();
let active = true;
// 在 await 前注册,覆盖源变化和侦听器停止时的清理。
onCleanup(() => {
active = false;
controller.abort();
});
loading.value = true;
error.value = '';
user.value = null;
try {
const response = await fetch(`/api/users/${encodeURIComponent(id)}`, {
signal: controller.signal,
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
if (active) user.value = data;
} catch (err) {
if (active) error.value = String(err);
} finally {
if (active) loading.value = false;
}
}, { immediate: true });
</script>
<template>
<p v-if="loading">加载中</p>
<p v-else-if="error" role="alert">{{ error }}</p>
<p v-else>{{ displayName }}</p>
</template>讲解重点:getter 监听 prop,computed 只派生展示;清理阻止旧结果写入,也保护 finally 不会清除新请求的 loading。示例使用第三参数 onCleanup,避免把 3.5+ 的 onWatcherCleanup 误当作所有 Vue 3 版本都可用。本例面向客户端,不包含 SSR 缓存或路由加载集成。
渲染更新概念图
Vue 2 也具备模板编译、VNode 和异步更新,图中的整体链路并非 Vue 3 独占。Vue 3 编译优化细节见 渲染机制。
建议复习顺序
- 第一轮:1~19 题,掌握 API、组件契约和侦听边界。
- 第二轮:20~26 题,串起生命周期、缓存与路由复用。
- 第三轮:27~37 题,解释依赖跟踪、编译优化与 Diff。
- 第四轮:38~43 题,结合对照表准备 Vue 2/3 迁移回答。