Vue2 vs Vue3 差异在哪里,我们一起来聊聊

307 阅读3分钟

📊 总体架构对比

graph TB
    subgraph "Vue 2 架构"
        V2[Vue 2 Constructor]
        V2 --> V2Obs[Observer 响应式系统]
        V2 --> V2Comp[Options API 组件]
        V2 --> V2VDom[VNode 虚拟DOM]
        V2 --> V2Compiler[Template 编译器]
        
        V2Obs --> V2DefProp[Object.defineProperty]
        V2Comp --> V2Life[生命周期钩子]
        V2VDom --> V2Patch[Patch 算法]
    end
    
    subgraph "Vue 3 架构"
        V3[Vue 3 createApp]
        V3 --> V3Proxy[Proxy 响应式系统]
        V3 --> V3Comp[Composition API]
        V3 --> V3VDom[VNode with PatchFlags]
        V3 --> V3Compiler[优化编译器]
        
        V3Proxy --> V3Reactive[reactive/ref]
        V3Comp --> V3Setup[setup 函数]
        V3VDom --> V3StaticHoist[静态提升]
    end

🔄 响应式系统对比

Vue 2 响应式原理

核心实现:Object.defineProperty

// Vue 2 源码:src/core/observer/index.ts
export function defineReactive(
  obj: object,
  key: string,
  val?: any,
  customSetter?: Function | null,
  shallow?: boolean,
  mock?: boolean
) {
  const dep = new Dep()
  
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend() // 依赖收集
      }
      return value
    },
    set: function reactiveSetter(newVal) {
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      val = newVal
      dep.notify() // 派发更新
    }
  })
}

Vue 3 响应式原理

核心实现:Proxy

// Vue 3 源码:packages/reactivity/src/reactive.ts
export function reactive(target: object) {
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap
  )
}

// packages/reactivity/src/baseHandlers.ts
class MutableReactiveHandler extends BaseReactiveHandler {
  get(target: object, key: string | symbol, receiver: object) {
    // 依赖收集
    track(target, TrackOpTypes.GET, key)
    
    const res = Reflect.get(target, key, receiver)
    
    // 深度响应式
    if (isObject(res)) {
      return reactive(res)
    }
    
    return res
  }
  
  set(target: object, key: string | symbol, value: unknown, receiver: object) {
    const oldValue = target[key]
    const result = Reflect.set(target, key, value, receiver)
    
    // 派发更新
    if (hasChanged(value, oldValue)) {
      trigger(target, TriggerOpTypes.SET, key, value, oldValue)
    }
    
    return result
  }
}

响应式系统对比图

flowchart LR
    subgraph "Vue 2 响应式"
        A[Object.defineProperty] --> B[getter/setter]
        B --> C[Dep 依赖收集]
        C --> D[Watcher 观察者]
        D --> E[组件更新]
        
        F["❌ 限制"]
        F --> G["无法检测数组索引变化"]
        F --> H["无法检测对象属性添加/删除"]
        F --> I["需要递归遍历所有属性"]
    end
    
    subgraph "Vue 3 响应式"
        J[Proxy] --> K["拦截所有操作"]
        K --> L[track 依赖收集]
        L --> M[trigger 派发更新]
        M --> N[组件更新]
        
        O["✅ 优势"]
        O --> P["完整的数组支持"]
        O --> Q["动态属性添加/删除"]
        O --> R["懒代理,按需响应式"]
    end

🧩 组件系统对比

Vue 2 Options API

// Vue 2 组件定义
export default {
  data() {
    return {
      count: 0,
      message: 'Hello'
    }
  },
  computed: {
    doubleCount() {
      return this.count * 2
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  mounted() {
    console.log('组件已挂载')
  }
}

Vue 3 Composition API

// Vue 3 组件定义
import { ref, computed, onMounted } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const message = ref('Hello')
    
    const doubleCount = computed(() => count.value * 2)
    
    const increment = () => {
      count.value++
    }
    
    onMounted(() => {
      console.log('组件已挂载')
    })
    
    return {
      count,
      message,
      doubleCount,
      increment
    }
  }
}

组件系统架构对比

graph TD
    subgraph "Vue 2 组件系统"
        A[Vue Constructor] --> B[initMixin]
        A --> C[stateMixin]
        A --> D[eventsMixin]
        A --> E[lifecycleMixin]
        A --> F[renderMixin]
        
        G[Options API] --> H[data]
        G --> I[computed]
        G --> J[methods]
        G --> K[生命周期钩子]
        
        L["❌ 问题"]
        L --> M["逻辑分散"]
        L --> N["复用困难"]
        L --> O["TypeScript 支持差"]
    end
graph TD
    subgraph "Vue 3 组件系统"
        P[createApp] --> Q[ComponentInternalInstance]
        Q --> R[setup 函数]
        
        S[Composition API] --> T[ref/reactive]
        S --> U[computed]
        S --> V[生命周期 hooks]
        S --> W[自定义 hooks]
        
        X["✅ 优势"]
        X --> Y["逻辑聚合"]
        X --> Z["更好的复用"]
        X --> AA["完整 TypeScript 支持"]
    end

🔄 生命周期对比

生命周期映射表

Vue 2Vue 3说明
beforeCreatesetup()组件创建前
createdsetup()组件创建后
beforeMountonBeforeMount挂载前
mountedonMounted挂载后
beforeUpdateonBeforeUpdate更新前
updatedonUpdated更新后
beforeDestroyonBeforeUnmount卸载前
destroyedonUnmounted卸载后
errorCapturedonErrorCaptured错误捕获
-onRenderTracked渲染跟踪
-onRenderTriggered渲染触发

生命周期实现对比

sequenceDiagram
    participant V2 as Vue 2 实例
    participant V3 as Vue 3 组件
    
    Note over V2: Options API 生命周期
    V2->>V2: beforeCreate
    V2->>V2: created
    V2->>V2: beforeMount
    V2->>V2: mounted
    
    Note over V3: Composition API 生命周期
    V3->>V3: setup()
    V3->>V3: onBeforeMount()
    V3->>V3: onMounted()
    
    Note right of V3: 更灵活的钩子注册

🎨 虚拟DOM对比

Vue 2 VNode 结构

// Vue 2 VNode 类
export default class VNode {
  tag?: string
  data: VNodeData | undefined
  children?: Array<VNode> | null
  text?: string
  elm: Node | undefined
  context?: Component
  componentOptions?: VNodeComponentOptions
  componentInstance?: Component
  
  constructor(
    tag?: string,
    data?: VNodeData,
    children?: Array<VNode> | null,
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions
  ) {
    // 初始化属性
  }
}

Vue 3 VNode 优化

// Vue 3 VNode 接口
export interface VNode<
  HostNode = RendererNode,
  HostElement = RendererElement,
  ExtraProps = { [key: string]: any }
> {
  type: VNodeTypes
  props: (VNodeProps & ExtraProps) | null
  key: string | number | symbol | null
  ref: VNodeNormalizedRef | null
  children: VNodeNormalizedChildren
  component: ComponentInternalInstance | null
  
  // 优化标记
  patchFlag: number
  dynamicProps: string[] | null
  dynamicChildren: VNode[] | null
}

虚拟DOM优化对比

graph LR
    subgraph "Vue 2 虚拟DOM"
        A["全量 diff"] --> B["递归比较所有节点"]
        B --> C["性能瓶颈"]
        
        D["静态节点"] --> E["每次都参与 diff"]
    end
    
    subgraph "Vue 3 虚拟DOM"
        F["PatchFlags 标记"] --> G["只 diff 动态内容"]
        G --> H["性能提升"]
        
        I["静态提升"] --> J["静态节点缓存"]
        J --> K["Block Tree"]
        K --> L["靶向更新"]
    end

⚡ 编译器优化对比

Vue 2 编译流程

flowchart LR
    A[Template] --> B[Parse AST]
    B --> C[Optimize]
    C --> D[Generate Code]
    D --> E[Render Function]
    
    F["优化有限"]
    F --> G["标记静态节点"]
    F --> H["静态根节点优化"]

Vue 3 编译优化

flowchart LR
    A[Template] --> B[Parse AST]
    B --> C[Transform]
    C --> D["多种优化"]
    D --> E[Generate Code]
    E --> F[Optimized Render]
    
    G["编译时优化"]
    G --> H["静态提升"]
    G --> I["PatchFlag 标记"]
    G --> J["内联组件 props"]
    G --> K["死代码消除"]

📦 打包体积对比

体积对比图

image.png

Vue 2:

  • Runtime Only: ~34KB
  • Runtime + Compiler: ~76KB
  • 完整版本: ~120KB

Vue 3:

  • Runtime Only: ~16KB (Tree-shaking 后)
  • Runtime + Compiler: ~47KB
  • 完整版本: ~84KB

🚀 性能对比

性能提升图表

image.png

性能优化点

优化项Vue 2Vue 3提升
响应式系统Object.definePropertyProxy2x
虚拟DOM全量 diffPatchFlag + 静态提升2.5x
编译优化基础优化多重编译时优化3x
Tree-shaking不支持完全支持50% 体积减少
TypeScript部分支持原生支持完整类型推导

🔧 API 变化总结

新增 API

Composition API:

  • ref() / reactive()
  • computed()
  • watch() / watchEffect()
  • onMounted() 等生命周期钩子
  • provide() / inject()
  • defineComponent()
  • defineProps() / defineEmits()

工具函数:

  • toRef() / toRefs()
  • unref()
  • isRef() / isReactive()
  • nextTick()
  • createApp()

移除的 API

  • Vue.config.productionTip
  • Vue.extend()
  • Vue.component() 全局注册方式改变
  • $children 实例属性
  • $listeners (合并到 $attrs)
  • 过滤器 (Filters)
  • 内联模板 (inline-template)

🎯 迁移建议

渐进式迁移策略

flowchart TD
    A["Vue 2 项目"] --> B{"评估项目规模"}
    
    B -->|小型项目| C["直接升级 Vue 3"]
    B -->|中型项目| D["使用 @vue/compat"]
    B -->|大型项目| E["分模块迁移"]
    
    C --> F["更新依赖"]
    D --> G["兼容模式运行"]
    E --> H["新功能用 Vue 3"]
    
    F --> I["测试验证"]
    G --> J["逐步移除兼容"]
    H --> K["老功能保持 Vue 2"]
    
    I --> L["部署上线"]
    J --> L
    K --> M["最终统一"]
    M --> L

关键迁移步骤

  1. 依赖更新

    npm install vue@next
    npm install @vue/compiler-sfc@next
    
  2. 构建工具配置

    // vite.config.js
    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    
    export default defineConfig({
      plugins: [vue()]
    })
    
  3. 入口文件修改

    // Vue 2
    import Vue from 'vue'
    import App from './App.vue'
    
    new Vue({
      render: h => h(App)
    }).$mount('#app')
    
    // Vue 3
    import { createApp } from 'vue'
    import App from './App.vue'
    
    createApp(App).mount('#app')
    

📊 总结对比表

特性Vue 2Vue 3优势
响应式Object.definePropertyProxy更强大的拦截能力
组件APIOptions APIComposition API更好的逻辑复用
TypeScript部分支持原生支持完整类型推导
性能基准2-3x 提升编译时优化
体积较大更小Tree-shaking
生态成熟稳定快速发展向后兼容
学习成本较低中等需要学习新概念

🔮 未来展望

Vue 3 的优势:

  • 更好的性能表现
  • 更强的 TypeScript 支持
  • 更灵活的组合式 API
  • 更小的打包体积
  • 更好的 Tree-shaking

建议:

  • 新项目直接使用 Vue 3
  • 现有项目可考虑渐进式迁移
  • 充分利用 Composition API 的优势
  • 关注 Vue 3 生态的发展

本文档基于 Vue 2.7.x 和 Vue 3.4.x 版本源码分析整理 欢迎指正错误