这里写目录标题
- 一、Vue2和Vue3区别概览
- 二、开发层表面差异:直观感知的功能变化
- 1、API 风格:Options API vs Composition API
- 2、模板语法:更灵活的渲染能力
- 🔥 核心差异速查
- 1. 根元素
- 2. v-model
- 3. v-if与v-for优先级
- 4. 插槽语法
- 5. 过滤器
- 6. 事件修饰符
- 7. 自定义指令
- 8. 样式深度选择器
- 变化示例
- Vue 2 → Vue 3 示例
- 性能优势
- 3、生命周期钩子:调整与新增
- 1. 钩子重命名(重要!)
- 2. Composition API 新格式
- 3 对照表
- 4、 关键点
- 1. setup() 替代两个钩子
- 2. 执行顺序
- 3. 使用建议
- 5、总结
- 三、 Vue 2 与 Vue 3 框架层核心代码差异
- 1、 核心架构对比
- 2、 底层实现重构详解
- 1. 响应式系统重写
- Vue 2:Object.defineProperty
- Vue 3:Proxy
- 2. 虚拟 DOM 优化
- Vue 2:全量 Diff
- Vue 3:编译时优化 + Patch Flag
- 3. 编译器重构
- Vue 2:运行时编译
- Vue 3:编译时优化
- 4. 源码架构重构
- vue 2:Monolithic
- Vue 3:Monorepo + 模块化
- 5. TypeScript 全面支持
- Vue 2:后期添加
- Vue 3:原生支持
- 6. 自定义渲染器
- Vue 2:复杂实现
- Vue 3:简化设计
- 7、架构设计理念变化
- 从 Options API 到 Composition API
- 从运行时到编译时
- 8、 总结
- 1. 性能飞跃
- 2. 开发体验
- 3. 扩展性
Vue 3 代表了 Vue 框架的一次重大飞跃。它在性能、开发体验(特别是 Composition API 和 TS)、灵活性和功能上都带来了显著的提升。虽然 Composition API 需要一些时间适应,但其带来的逻辑组织、复用和维护性的优势对于构建现代、复杂的前端应用是至关重要的。对于新项目,Vue 3 是毋庸置疑的选择;对于老项目,应积极评估并规划向 Vue 3 的迁移,Vue2在2023年底已经停止维护。
一、Vue2和Vue3区别概览
| 特性 | Vue2 | Vue3 |
|---|---|---|
| 架构&响应式 | Options API(选项式API主导) | Composition API(组合式API主导)+ Options API |
| 响应式原理 | Object.defineProprety数据劫持 | Proxy数据代理 |
| 性能 | 良好 | 显著提升 |
| 构建工具 | Vue CLI(基于 Webpack) | Vite |
| 状态管理 | VueX | Pinia |
| TypeScript | 弱支持,需额外配置 | 原生支持 |
| Fragment | 单根节点限制 | 支持多根节点 |
| Tree-Shaking | 有限支持 | 原生支持 |
| Teleport | 无 | 内置 () |
| Suspense | 无 | 内置 |
| 全局 API | Vue 构造函数挂载,全局配置 | 创建应用实例(createApp),避免全局污染 |
| 事件 API | $on, $off, $once | 删除 |
| v-model | 1个组件仅1个 v-model | 支持多个 v-model (带参数),组件双向绑定更灵活 |
| 指令钩子 | bind, inserted 等 | 与组件生命周期对齐,命名和语义更一致 |
二、开发层表面差异:直观感知的功能变化
Vue2 与 Vue3 在日常开发中最直观的差异,集中在 API 风格、模板语法、生命周期等高频使用场景,这些变化直接影响开发效率与代码组织方式。
1、API 风格:Options API vs Composition API
这是 Vue2 与 Vue3 最核心的开发体验差异,决定了代码的组织逻辑。
| 特性 | Vue2(Options API) | Vue3(Composition API + Options API) |
|---|---|---|
| 代码组织方式 | 按 “选项” 划分代码:将数据(data)、方法(methods)、计算属性(computed)等拆分到不同选项中,逻辑分散在多个选项里。 | 按 “功能逻辑” 聚合代码:通过setup()函数或<script setup>语法,将同一业务逻辑(如表单验证、数据请求)的代码集中在一起,无需跨选项查找。 |
| 代码复用 | 依赖 Mixins、Scoped Slots 实现,但存在命名冲突、逻辑来源不清晰等问题(如多个 Mixins 都定义了handleClick方法)。 | 通过组合式函数(Composables)复用逻辑,如useRequest()、useForm(),逻辑来源明确,无命名冲突,可灵活组合。 |
| TypeScript 支持 | 需通过vue-class-component等第三方库间接支持,类型推导不完整,配置复杂。 | 原生支持 TypeScript,setup()函数、响应式 API(ref/reactive)均能提供完整类型推导,无需额外配置。 |
代码示例对比:实现 “计数器 + 数据请求” 的简单功能
<!--Vue2(OptionsAPI):逻辑分散在多个选项--><template><div>{{count}}<button @click="add">+1</button></div><div>{{user.name}}</div></template><script>exportdefault{data(){return{count:0,user:{}// 类型无法自动推导}},methods:{add(){this.count++// 依赖this上下文,容易出现指向问题},fetchUser(){axios.get('/api/user').then(res=>{this.user=res.data})}},mounted(){this.fetchUser()// 生命周期与方法分离}}</script><!--Vue3(<script setup>+CompositionAPI):逻辑聚合--><template><div>{{count}}<button @click="add">+1</button></div><div>{{user.name}}</div></template><script setup lang="ts">import{ref,onMounted}from'vue'importaxiosfrom'axios'// 计数器逻辑(聚合)constcount=ref(0)// 明确的响应式变量,TypeScript自动推导为number类型constadd=()=>{count.value++// 直接操作变量,无this依赖}// 用户数据请求逻辑(聚合)constuser=ref<{name:string}>({name:''})// 显式定义类型constfetchUser=async()=>{constres=awaitaxios.get('/api/user')user.value=res.data}// 生命周期与逻辑绑定onMounted(fetchUser)</script>2、模板语法:更灵活的渲染能力
Vue 3 模板更灵活、更强大、更简洁
🔥 核心差异速查
1. 根元素
- Vue 2❌ 必须单根元素
- Vue 3✅ 支持多根(Fragment)
2. v-model
- Vue 2:单个
v-model(触发input事件) - Vue 3:多个
v-model:xxx(触发update:xxx事件)
3. v-if与v-for优先级
- Vue 2:
v-for>v-if - Vue 3:
v-if>v-for
4. 插槽语法
- Vue 2:
slot="name"、slot-scope - Vue 3:统一
v-slot:name(简写#name)
5. 过滤器
- Vue 2✅ 支持:
{{ text | filter }} - Vue 3❌ 移除:用方法或计算属性替代
6. 事件修饰符
- Vue 2:基本修饰符
- Vue 3:新增
.middle、.right,移除按键码支持
7. 自定义指令
- Vue 2:
bind、inserted、update - Vue 3:对齐组件生命周期
mounted、updated
8. 样式深度选择器
- Vue 2:
>>>、/deep/、::v-deep - Vue 3:统一
:deep()、:slotted()、:global()
变化示例
Vue 2 → Vue 3 示例
<!-- Vue 2 --><template><div><!-- 必须包裹 --><Childv-model="data"><templateslot="header">标题</template><p>{{ text | capitalize }}</p></Child></div></template><!-- Vue 3 --><template><Childv-model:data="data"><!-- 多根元素 --><template#header>标题</template><!-- 新插槽语法 --><p>{{ capitalize(text) }}</p><!-- 无过滤器 --></Child></template>- 移除不必要的
<div>包装 - 插槽语法
slot→# - 过滤器改为方法调用
- 检查
v-if/v-for逻辑 - 使用新的
v-model语法
性能优势
- 更小的包体积
- 更好的Tree-shaking
- 更快的渲染速度
- 更少的内存占用
结论:Vue 3模板语法更现代化、更灵活,新项目应直接使用,老项目可逐步迁移。
3、生命周期钩子:调整与新增
1. 钩子重命名(重要!)
Vue 2 → Vue 3: beforeDestroy → beforeUnmount destroyed → unmounted2. Composition API 新格式
// Vue 2(Options API)exportdefault{mounted(){/* ... */},destroyed(){/* ... */}}// Vue 3(Composition API)import{onMounted,onUnmounted}from'vue'setup(){onMounted(()=>{/* ... */})onUnmounted(()=>{/* ... */})}3 对照表
| Vue 2 钩子 | Vue 3 Options API | Vue 3 Composition API |
|---|---|---|
| beforeCreate | ✅ 保留 | ❌ 移除(用 setup()) |
| created | ✅ 保留 | ❌ 移除(用 setup()) |
| beforeMount | ✅ 保留 | onBeforeMount |
| mounted | ✅ 保留 | onMounted |
| beforeUpdate | ✅ 保留 | onBeforeUpdate |
| updated | ✅ 保留 | onUpdated |
| beforeDestroy | beforeUnmount | onBeforeUnmount |
| destroyed | unmounted | onUnmounted |
| activated | ✅ 保留 | onActivated |
| deactivated | ✅ 保留 | onDeactivated |
| errorCaptured | ✅ 保留 | onErrorCaptured |
4、 关键点
1. setup() 替代两个钩子
// Vue 2beforeCreate(){/* 组件初始化前 */}created(){/* 组件创建完成 */}// Vue 3setup(){/* 替代上面两个钩子 */}2. 执行顺序
// 组件启动:setup()→ beforeMount → mounted// 组件销毁:beforeUnmount → unmounted3. 使用建议
- 新项目:用 Composition API (
onXxx) - 老项目:先改名称 (
destroyed→unmounted) - 清理资源:在
onUnmounted中清理定时器/监听器
5、总结
“销毁改卸载,组合API用on前缀,setup替代创建钩子”
关键变化:Vue3移除了beforeDestroy/destroyed,统一为beforeUnmount/unmounted,语义更准确;同时Composition API的钩子需从vue中导入,避免与Options API混淆。
三、 Vue 2 与 Vue 3 框架层核心代码差异
1、 核心架构对比
| 维度 | Vue 2 | Vue 3 |
|---|---|---|
| 源码组织 | 单文件、Flow 类型 | 模块化、TypeScript |
| 响应式系统 | Object.defineProperty | Proxy |
| 虚拟 DOM | 全量 Diff | Patch Flag 优化 Diff |
| 模板编译 | 运行时编译 | 编译时优化 |
| 源码体积 | 完整打包 | 模块化、Tree-shaking |
| 性能指标 | 基准参考 | 快 1.3-2 倍 |
2、 底层实现重构详解
1. 响应式系统重写
Vue 2:Object.defineProperty
// 核心原理functiondefineReactive(obj,key,val){Object.defineProperty(obj,key,{enumerable:true,configurable:true,get(){// 依赖收集dep.depend()returnval},set(newVal){if(newVal===val)return// 触发更新val=newVal dep.notify()}})}// ❌ 局限:// 1. 无法检测对象新增/删除属性// 2. 数组变异需要拦截原型方法// 3. 需要 Vue.set/Vue.delete// 4. 递归遍历性能开销大Vue 3:Proxy
// 核心原理functionreactive(obj){returnnewProxy(obj,{get(target,key,receiver){// 依赖收集track(target,key)returnReflect.get(target,key,receiver)},set(target,key,value,receiver){// 触发更新constresult=Reflect.set(target,key,value,receiver)trigger(target,key)returnresult},deleteProperty(target,key){// 支持删除属性constresult=Reflect.deleteProperty(target,key)trigger(target,key)returnresult}})}// ✅ 优势:// 1. 完整对象监听// 2. 更好的数组支持// 3. 支持 Map、Set// 4. 惰性代理(用到才监听)// 5. 性能更好2. 虚拟 DOM 优化
Vue 2:全量 Diff
// 简单对比算法functionpatch(oldVNode,newVNode){// 全量比较所有属性if(oldVNode.tag!==newVNode.tag){// 替换整个节点}// 深度递归比较子节点updateChildren(oldVNode.children,newVNode.children)}// ❌ 问题:// 1. 不必要的递归// 2. 静态内容重复比对// 3. 组件更新粒度粗Vue 3:编译时优化 + Patch Flag
// 编译时生成优化提示constvnode={type:'div',props:{id:'app',class:normalizeClass({active:isActive})},children:[{type:'h1',children:'静态标题',patchFlag:1},// 静态{type:'p',children:dynamicText,patchFlag:2},// 文本动态{type:'button',props:{onClick:handler},patchFlag:8// 只有事件变化}]}// Patch Flag 类型constPatchFlags={TEXT:1,// 动态文本CLASS:2,// 动态 classSTYLE:4,// 动态 stylePROPS:8,// 动态 props(不含 class/style)FULL_PROPS:16,// 动态 key 或其他HYDRATE_EVENTS:32}// ✅ 优化效果:// 1. 静态节点提升(只创建一次)// 2. 动态节点标记(只更新变化部分)// 3. 树结构打平(减少递归层级)// 4. 事件缓存(避免重复绑定)3. 编译器重构
Vue 2:运行时编译
// 主要工作:// 1. 解析模板 → AST// 2. 优化静态节点// 3. 生成渲染函数(运行时)// 生成代码示例:functionrender(){with(this){return_c('div',{attrs:{id:'app'}},[_c('h1',[_v(_s(message))]),_c('button',{on:{click:onClick}},[_v('点击')])])}}// ❌ 问题:// 1. 运行时编译开销// 2. with 语句性能差// 3. 无法深度优化Vue 3:编译时优化
// 主要优化:// 1. 模块化编译器(@vue/compiler-core)// 2. 源码映射(Source Map)// 3. 更好的错误提示// 4. Tree-shaking 支持// 生成的优化代码:import{createElementVNodeas_createElementVNode,toDisplayStringas_toDisplayString,openBlockas_openBlock,createElementBlockas_createElementBlock}from'vue'exportfunctionrender(_ctx,_cache){return(_openBlock(),_createElementBlock('div',{id:'app'},[// 静态节点提升到外部_hoisted_1,// 动态节点带 Patch Flag_createElementVNode('p',null,_toDisplayString(_ctx.message),1/* TEXT */)]))}// 静态节点提升到渲染函数外const_hoisted_1=/*#__PURE__*/_createElementVNode('h1',null,'静态标题',-1/* HOISTED */)4. 源码架构重构
vue 2:Monolithic
vue/ ├── src/ │ ├── core/ # 核心 │ ├── compiler/ # 编译器 │ ├── platforms/ # 平台相关 │ └── shared/ # 共享代码 └── dist/ ├── vue.js # 完整版(带编译器) ├── vue.runtime.js # 运行时版 └── vue.min.js # 压缩版 // ❌ 问题: // 1. 代码耦合度高 // 2. Tree-shaking 困难 // 3. 维护成本高Vue 3:Monorepo + 模块化
vue-next/ ├── packages/ │ ├── compiler-core/ # 编译核心 │ ├── compiler-dom/ # DOM 编译 │ ├── runtime-core/ # 运行时核心 │ ├── runtime-dom/ # DOM 运行时 │ ├── reactivity/ # 响应式系统 ← 可单独使用! │ ├── shared/ # 共享工具 │ └── vue/ # 主包(整合) └── scripts/ ├── build.js # 构建脚本 └── dev.js # 开发脚本 // ✅ 优势: // 1. 模块化设计 // 2. 更好的 Tree-shaking // 3. 响应式系统可独立使用 // 4. 更容易维护和扩展5. TypeScript 全面支持
Vue 2:后期添加
// 需要额外的类型声明importVuefrom'vue'// 组件选项类型exportdefaultVue.extend({props:{msg:String},data(){return{count:0}}})// ❌ 问题:// 1. this 类型推导有限// 2. 装饰器语法复杂// 3. 与 Vue API 集成度低Vue 3:原生支持
// 源码完全用 TypeScript 编写import{defineComponent,ref}from'vue'exportdefaultdefineComponent({props:{msg:{type:String,required:true}},setup(props){// 完美的类型推断constcount=ref(0)// props 类型安全console.log(props.msg.toUpperCase())return{count}}})// ✅ 优势:// 1. 源码即类型// 2. 更好的 IDE 支持// 3. Composition API 类型友好// 4. 减少运行时错误6. 自定义渲染器
Vue 2:复杂实现
// 需要实现完整渲染接口classMyRenderer{createElement(){/* ... */}insert(){/* ... */}remove(){/* ... */}// ... 数十个方法}// 使用复杂Vue.createRenderer=function(){returnnewMyRenderer()}Vue 3:简化设计
// 核心接口简化import{createRenderer}from'@vue/runtime-core'const{createApp}=createRenderer({patchProp,// 处理属性insert,// 插入节点remove,// 移除节点createElement,// 创建元素createText,// 创建文本createComment,// 创建注释setText,// 设置文本setElementText,// 设置元素文本parentNode,// 父节点nextSibling// 下一个兄弟节点})// 轻松支持多平台// - @vue/runtime-dom (Web)// - @vue/runtime-test (测试)// - @vue/runtime-canvas (Canvas)// - 自定义渲染器(如小程序、Native)7、架构设计理念变化
从 Options API 到 Composition API
// Vue 2:基于选项的组织// 逻辑分散在 data、methods、computed 中exportdefault{data(){return{x:0,y:0}},mounted(){window.addEventListener('mousemove',this.update)},destroyed(){window.removeEventListener('mousemove',this.update)},methods:{update(e){this.x=e.x;this.y=e.y}}}// Vue 3:基于逻辑的组合// 相关逻辑组织在一起import{ref,onMounted,onUnmounted}from'vue'functionuseMouse(){constx=ref(0)consty=ref(0)constupdate=e=>{x.value=e.x;y.value=e.y}onMounted(()=>window.addEventListener('mousemove',update))onUnmounted(()=>window.removeEventListener('mousemove',update))return{x,y}}// 逻辑复用更自然exportdefault{setup(){const{x,y}=useMouse()return{x,y}}}从运行时到编译时
// 编译时发现更多优化机会constCompilerOptimizations={静态提升:'将静态节点提到渲染函数外',树结构打平:'减少虚拟DOM层级',缓存事件处理器:'避免重复创建函数',PatchFlag标记:'只更新动态部分',内联函数:'减少函数调用开销'}8、 总结
1. 性能飞跃
- ✅Proxy 响应式:性能更好,支持完整对象
- ✅编译时优化:静态提升、Patch Flag
- ✅Tree-shaking:按需导入,包更小
2. 开发体验
- ✅TypeScript 原生支持:更好的类型推导
- ✅组合式 API:逻辑复用更灵活
- ✅模块化设计:维护和扩展更容易
3. 扩展性
- ✅自定义渲染器:支持多平台
- ✅独立响应式包:可单独使用
- ✅渐进式升级:兼容 Vue 2 大部分 API
Vue 3 是一次彻底的重构,在保持 API 兼容性的同时,拥抱现代 JavaScript 特性,通过架构层面的优化实现了性能的质的飞跃。