当前位置: 首页 > news >正文

vue2 + vue3差异点

Vue 2 到 Vue 3 是一次底层架构全面重写,同时带来了大量新特性和思维方式的转变。下面从核心机制、API 设计、编译优化、全局特性等维度,逐一深入对比,并结合源码逻辑或伪代码辅助理解。


1. 响应式系统:从Object.definePropertyProxy

Vue 2 响应式原理

  • 使用Object.defineProperty劫持对象属性的 getter/setter。
  • 初始化时递归遍历所有属性,为每个属性单独设置 getter/setter,依赖收集和派发更新。
  • 缺点:
    • 无法检测对象属性的添加/删除(需用Vue.set/delete)。
    • 对数组的索引赋值和length修改无法监听,通过重写数组的 7 种变更方法弥补。
    • 深层递归遍历性能开销大,初始化慢。

简化源码思路:

functiondefineReactive(obj,key,val){constdep=newDep()Object.defineProperty(obj,key,{get(){dep.depend()returnval},set(newVal){val=newVal dep.notify()}})}

Vue 3 响应式原理

  • 使用 ES6Proxy代理整个对象,可拦截 13 种操作。
  • 依赖收集通过track,触发通过trigger,使用WeakMapMapSet结构存储依赖。
  • 优点:
    • 可监听属性的增加、删除。
    • 直接监听数组索引和length
    • 惰性响应式:只有访问到的深层属性才会被递归代理(reactive仅在 get 时递归)。
    • 支持MapSetWeakMapWeakSet等复杂类型。

核心代码模拟:

functionreactive(target){returnnewProxy(target,{get(target,key,receiver){constres=Reflect.get(target,key,receiver)track(target,key)// 依赖收集returnisObject(res)?reactive(res):res// 懒递归},set(target,key,val,receiver){constoldVal=target[key]constresult=Reflect.set(target,key,val,receiver)if(val!==oldVal)trigger(target,key)returnresult},deleteProperty(target,key){consthad=Object.prototype.hasOwnProperty.call(target,key)constresult=Reflect.deleteProperty(target,key)if(had)trigger(target,key,'delete')returnresult}})}

ref则是对基本类型值包裹一个对象,通过getter/setter访问.value,内部仍然走reactive的逻辑处理对象值。


2. Composition API vs Options API

Vue 2 Options API

  • 组件逻辑按选项(data,methods,computed,watch, 生命周期)分割。
  • 代码复用主要靠 mixin,存在命名冲突、来源不清晰等问题。

Vue 3 Composition API

  • 按逻辑关系组织代码,一个功能的响应式状态、方法、监听集中在一起。
  • 逻辑复用通过自定义组合函数(hooks),没有 mixin 的冲突。
  • setup函数为入口,在组件实例创建前执行,this不可用。
  • 提供ref,reactive,computed,watch,watchEffect, 生命周期钩子(onMounted等)。

示例对比:

// Vue 2 Options APIexportdefault{data(){return{count:0}},computed:{double(){returnthis.count*2}},methods:{increment(){this.count++}}}// Vue 3 Composition APIimport{ref,computed}from'vue'exportdefault{setup(){constcount=ref(0)constdouble=computed(()=>count.value*2)constincrement=()=>count.value++return{count,double,increment}}}

Vue 3 也完全兼容 Options API,两者可在同一组件中使用,但 Composition API 能更好地组织和复用逻辑,并且天然支持 Tree-shaking。


3. 生命周期钩子变化

Vue 2Vue 3 Composition 钩子说明
beforeCreate不需要,相当于setup()开始此时组件实例刚创建,data未初始化,Vue 3 中setup执行时机替代它
created不需要setup末尾可替代
beforeMountonBeforeMount模板编译/渲染前
mountedonMountedDOM 挂载后
beforeUpdateonBeforeUpdate数据更新前
updatedonUpdated数据更新导致 DOM 更新后
beforeDestroyonBeforeUnmount组件销毁前(Vue 3 重命名为 unmount)
destroyedonUnmounted组件销毁后
errorCapturedonErrorCaptured捕获子组件错误

setup中可同步使用这些钩子函数,也新增了onRenderTrackedonRenderTriggered用于调试响应式依赖。


4. Fragments:多根节点支持

Vue 2:组件模板必须有且仅有一个根元素,否则报错。

<!-- Vue 2 会报错 --><template><div>A</div><div>B</div></template>

Vue 3:组件可返回多个根节点,内部自动创建 Fragment 虚拟节点包裹。

<!-- Vue 3 合法 --><template><div>A</div><div>B</div></template>

编译结果会生成一个Fragment类型的 vnode,patch时只处理子节点数组,不生成额外的 DOM 元素。这减少无意义的包裹 DOM,使组件更灵活。


5. Teleport(传送门)

Vue 3 新增内置组件<Teleport>,可将子节点渲染到 DOM 中特定目标位置,常用于模态框、通知等需要脱离父组件层级的场景。

<template><button@click="show = true">打开弹窗</button><Teleportto="body"><divv-if="show"class="modal">我是被传送到 body 下的内容</div></Teleport></template>

原理:Teleportprocess过程中,会根据to选择器找到目标容器,使用move方法将子节点移动过去,且保持响应式绑定。


6. Suspense(异步组件协调)

Vue 3 引入<Suspense>,用于协调异步组件,展示加载状态。

<Suspense><template#default><AsyncComponent/></template><template#fallback><div>Loading...</div></template></Suspense>

Suspense可以捕获setup中返回的Promise,在未 resolve 前显示 fallback,并支持嵌套、错误处理。这在 SSR 中实现流式传输也有重要作用。


7. 自定义渲染器(Custom Renderer)

Vue 2 的渲染器与平台(Web)强绑定,虽然通过 weex 有一些分离,但不够彻底。
Vue 3 将运行时拆分为@vue/runtime-core(平台无关)和@vue/runtime-dom(Web),通过自定义nodeOpspatchProp可以创建自定义渲染器,比如渲染到 Canvas、Native。

核心接口:

import{createRenderer}from'@vue/runtime-core'const{createApp}=createRenderer({createElement(type){...},insert(child,parent,anchor){...},patchProp(el,key,prev,next){...}})

这让 Vue 3 不局限于 DOM,可轻松移植到其他平台。


8. 虚拟 DOM 与编译优化

Vue 3 的编译器进行了大幅性能优化,生成的代码配合运行时,使得更新粒度更细。

  • 静态提升:模板中不变的节点提升到渲染函数外,避免每次重新创建 vnode。
  • 预字符串化:大量连续静态节点会被直接编译成 HTML 字符串,通过innerHTML一次性创建。
  • Patch Flags(补丁标记):动态节点打上标志位(如1代表文本动态,2代表 class 动态,32代表属性动态),更新时只比对所需部分,跳过静态内容。
  • 事件缓存:将事件处理函数缓存,避免重新绑定。
  • Block Tree:把动态节点收集到一个扁平数组中,更新时直接遍历该数组,跳过静态节点比对,diff 性能接近 O(n)(n 为动态节点数)。

编译示例:

<div><span>静态</span><span>{{ msg }}</span></div>

编译后的渲染函数近似:

import{createElementVNodeas_c,toDisplayStringas_t}from'vue'const_hoisted_1=_c('span',null,'静态')exportfunctionrender(_ctx){return(_openBlock(),_c('div',null,[_hoisted_1,_c('span',null,_t(_ctx.msg),1/* TEXT */)]))}

带有1 /* TEXT */的标志位,运行时只检查textContent


9. TypeScript 支持

Vue 2 使用 Flow 做类型检查,对 TS 支持依赖官方声明文件和vue-class-component,体验一般,类型推导差。
Vue 3 使用 TypeScript 重写整个源码,提供完善的类型定义和推导。

  • defineComponent提供完备的 Props、组件实例类型推导。
  • Composition API 中ref<T>()reactive<T>()等可正确推导类型。
  • defineProps,defineEmits<script setup>中纯类型声明,无需运行时定义。

示例:

constprops=defineProps<{title:string}>()

10. 全局 API 变化与 Tree-shaking

Vue 2全局 API 都挂载在Vue对象上:

importVuefrom'vue'Vue.component('MyComp',{...})Vue.directive('focus',{...})Vue.mixin({...})newVue({render:h=>h(App)}).$mount('#app')

这种方式将全部 API 打包在一起,无法 tree-shake,且多个应用共享同一个 Vue 全局配置,易相互污染。

Vue 3采用具名导出 +createApp工厂函数:

import{createApp,defineComponent,directive}from'vue'constapp=createApp(App)app.component('MyComp',{...})app.directive('focus',{...})app.mount('#app')

createApp返回独立的应用实例,配置隔离,互不影响。未使用的 API 如transition,provide等可以通过构建工具 Tree-shaking 移除。


11. v-model 变化

Vue 2

  • 一个组件只能有一个v-model,默认绑定valueprop 和input事件。
  • 自定义model选项:
    model:{prop:'visible',event:'change'}
  • .sync修饰符实现“双向绑定”其他 prop。

Vue 3

  • 移除model选项,统一为v-model的变体。
  • 可同时使用多个v-modelv-model:title,v-model:visible
  • 默认绑定modelValueprop 和update:modelValue事件。
  • 移除了.sync修饰符,由v-model:propName代替。
<!-- Vue 3 --><ChildComponentv-model:title="title"v-model="visible"/>

子组件:

props:['modelValue','title'],emits:['update:modelValue','update:title']

12. 事件总线(EventBus)移除

Vue 2 中常用new Vue()作为事件总线:

constbus=newVue()bus.$on('event',handler)bus.$emit('event',data)

Vue 3 移除了$on,$off,$once,官方推荐使用mitt等第三方库或provide/inject、全局状态管理代替,原因:实例的监听会造成内存泄漏风险,且不符合 Composition API 设计。


13. 函数式组件变化

Vue 2:函数式组件无状态、无实例,通过functional: true定义,render接收context

exportdefault{functional:true,render(h,ctx){returnh('div',ctx.props.msg)}}

Vue 3:函数式组件简化为普通函数,不再需要functional标志,性能与有状态组件差距极小(Vue 3 的组件初始化非常轻量)。

import{h}from'vue'constFuncComp=(props)=>h('div',props.msg)

14. 组件注册与异步组件

Vue 2局部注册仅支持选项对象,异步组件:

Vue.component('async-comp',()=>import('./Comp.vue'))

Vue 3通过defineComponent辅助类型推导,异步组件使用defineAsyncComponent

import{defineAsyncComponent}from'vue'constAsyncComp=defineAsyncComponent(()=>import('./Comp.vue'))

defineAsyncComponent支持高级选项:loadingComponent,errorComponent,delay,timeout等。


15. $attrs 与 $listeners

Vue 2$attrs包含未被 props 接收的 attribute,$listeners包含父组件传递的事件监听器(不含.native修饰符的)。
Vue 3

  • 移除$listeners,事件监听器现在也是$attrs的一部分,以onXxx的形式存在。
  • 移除.native修饰符,如果未在emits声明的事件,会被视为原生事件,可直接在$attrsonClick获取。

这统一了属性和事件透传,方便更高阶的组件封装。


16. 其他细节变化

  • key 属性:Vue 3 在<template v-for>上现在推荐(也可以)将key放在<template>标签上,不再必须放在子元素。
  • v-if vs v-for 优先级:Vue 2 中v-for优先级高于v-if;Vue 3 中v-if优先级更高,当两者同处一个元素时,v-if先评估,可能导致错误。
  • 渲染函数 API:Vue 2 使用render(h) { return h('div', ...) },Vue 3 直接从vue引入h
  • Transition 类名变化v-enter改为v-enter-fromv-leave改为v-leave-from,语义更清晰。

17. 源码架构与构建

Vue 3 采用 monorepo 管理,使用 TypeScript 重写,按功能拆分包:

  • @vue/reactivity— 响应式系统
  • @vue/runtime-core— 平台无关运行时
  • @vue/runtime-dom— Web 运行时
  • @vue/compiler-core— 平台无关编译器
  • @vue/compiler-dom— Web 编译器
  • @vue/runtime-test— 测试用渲染器

这种分层使得各模块可独立使用、测试和 Tree-shaking,也方便自定义渲染器。


总结

维度Vue 2Vue 3
响应式defineProperty,无法检测增删,数组需要 hackProxy,全面劫持,惰性收集,支持 Map/Set
API 风格Options API新增 Composition API,兼容 Options API
根节点单根多根 Fragment
类型支持Flow,TS 体验一般TypeScript 重写,完美类型推导
编译优化静态提升、Patch Flag、Block Tree 等
全局 APIVue构造函数,多应用共享配置createApp实例隔离,按需导出
传送/异步协调无内置Teleport、Suspense
自定义渲染耦合度高通过createRenderer自定义
v-model单个.sync补充多个v-model:attr,移除.sync
事件总线$on/$off/$emit移除,推荐 mitt 或 provide
包大小全量约 23KB gzip借助 tree-shaking 可更低 (约 10KB+)

Vue 3 从底层到上层全面进化,不仅带来了更好的性能、更优的 TypeScript 集成,也通过 Composition API 和丰富的内置组件极大提升了开发体验与代码组织能力。

http://www.jsqmd.com/news/954375/

相关文章:

  • 【X5】快速调试验证MIPI摄像头
  • 告别卡顿与闪退:Quartus II 13.1在Win10/Win11下的稳定运行与性能优化配置全攻略
  • MATLAB行星齿轮动力学仿真脚本:含ode45求解器与完整系统建模函数
  • 别再只调参了!遗传算法解VRP时,这3个编码细节才是性能关键
  • 你的产品能过EMC认证吗?一文搞懂CS/RS传导辐射抗扰、ESD静电、EFT群脉冲测试要求
  • 企业AI编程解决方案:2026最新权威AI编程工具必看开篇
  • 遗传算法工业实战:四大核心杠杆调优指南
  • 2026 张家界防水补漏三家品牌横向测评:厨卫屋面地下室修缮哪家靠谱?吉修匠 99.8 分五星稳居榜首 - 吉修匠
  • 给Jetson Nano B01换颗‘中国心’:手把手教你配置清华源并安装Python全家桶
  • MinerU2.5 Pro技术解析:1.2B参数SOTA PDF解析模型,完整部署教程(Transformers/vLLM/SGLang/Docker)
  • DenseNet实战:用TensorFlow 2.x在小型数据集上做图像分类,参数少效果也不错
  • 嵌入式新手福音,用快马生成带详解的dma示例代码,轻松攻克直接内存访问
  • 跳出传统 Agent 桎梏,浅析代码即智能体的底层运行逻辑与落地实践
  • 计算机毕业设计之基于Django和Vue的汽车销量数据分析系统的设计与实现
  • 不只是驱动问题:深度解析TI XDS100仿真器EEPROM数据损坏的根源与预防
  • C#上位机开发笔记:封装一个稳定可靠的欧姆龙NX PLC通信类库(附源码)
  • 新手福音:基于快马平台轻松上手吴恩达claude中文手册实践
  • 从‘炼丹’到‘工程’:深度学习中权重初始化和输入归一化的实战避坑指南
  • Anaconda安装后必做的三件事:验证、配环境变量、创建你的第一个Python 3.8虚拟空间
  • 别再死磕D-H参数了!用Matlab Robotic Toolbox 10.4快速复现一个四轴机械臂(附完整代码)
  • MuleSoft企业级AI编排:让大模型真正融入ERP/CRM核心业务流
  • LLM投毒:大模型数据层精准攻击与七道防御体系
  • 2026年高县亲子水上乐园选型指南:龙源溪山泉水乐园深度评测 - 企业名录优选推荐
  • 用NodeMCU和Blinker自制万能红外遥控器,手把手教你让旧家电秒变智能(附完整代码)
  • 不止是游戏!HMS Core 5.2.0的CG Kit体积云特效,还能这样用在你的App里
  • 2687183396@qq.com
  • 别再傻傻分不清了!SCI、EI、IEEE到底该投哪个?给研究生和工程师的选刊避坑指南
  • 正统传承视角下的汕头高端私房菜核心技术标准拆解 - 奔跑123
  • CST仿真后一键导入MATLAB做阵列加权综合:支持切比雪夫、泰勒等算法
  • 从自动驾驶到商品推荐:聊聊Smooth L1 Loss为何成了YOLO、Faster R-CNN的‘心头好’