Vue2 与 Vue3 核心区别深度解析:源码级详解

总述:架构革命性升级

Vue3 是对 Vue2 的全面重构而非简单更新,核心变化可概括为:

  1. 响应式系统Object.definePropertyProxy 重构
  2. 虚拟DOM:静态标记 + PatchFlag 优化
  3. 代码组织:Options API → Composition API
  4. 打包机制:支持 Tree-shaking 的模块化架构
  5. 类型系统:全面拥抱 TypeScript

以下通过源码对比详细解析核心差异:


一、响应式系统重构(核心变化)

1.1 Vue2 响应式实现

源码位置src/core/observer/index.js

// 简化版实现
export function defineReactive(obj, key) {
  const dep = new Dep()
  let val = obj[key]
  
  Object.defineProperty(obj, key, {
    get() {
      dep.depend()  // 收集依赖
      return val
    },
    set(newVal) {
      val = newVal
      dep.notify()  // 触发更新
    }
  })
}

缺陷分析

  • 递归遍历所有属性(性能差)
  • 无法检测新增/删除属性(需 Vue.set
  • 数组需要特殊处理(重写7个方法)

1.2 Vue3 响应式实现

源码位置packages/reactivity/src/reactive.ts

// 基于Proxy的实现
function createReactiveObject(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      track(target, key)  // 追踪依赖
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver)
      trigger(target, key)  // 触发更新
      return result
    },
    deleteProperty(target, key) {
      const result = Reflect.deleteProperty(target, key)
      trigger(target, key)  // 删除也能触发更新
      return result
    }
  })
}

优势对比

特性Vue2Vue3提升效果
检测能力仅限已有属性全属性无需Vue.set
数组支持需重写方法原生支持直接索引赋值
集合类型不支持Map/Set原生支持扩展数据结构支持
初始化性能O(n)递归遍历O(1)代理大型对象快40%

二、虚拟DOM优化(性能飞跃)

2.1 Vue2 Diff算法

源码位置src/core/vdom/patch.js

// 全量比较策略
function patchVnode(oldVnode, vnode) {
  // 1. 比较标签类型
  if (oldVnode.tag !== vnode.tag) {
    replaceVnode(oldVnode, vnode)
    return
  }
  
  // 2. 比较属性
  const oldProps = oldVnode.data.attrs || {}
  const newProps = vnode.data.attrs || {}
  updateAttrs(oldVnode.elm, oldProps, newProps)
  
  // 3. 比较子节点(递归)
  updateChildren(oldVnode.elm, oldVnode.children, vnode.children)
}

问题:即使静态内容也要遍历比较

2.2 Vue3 PatchFlag 优化

源码位置packages/runtime-core/src/vnode.ts

// PatchFlag 标记(二进制位)
export const enum PatchFlags {
  TEXT = 1,         // 动态文本
  CLASS = 1 << 1,   // 动态class
  STYLE = 1 << 2,   // 动态style
  PROPS = 1 << 3,   // 动态属性(非class/style)
  FULL_PROPS = 1 << 4, // 动态key需全量比较
  HYDRATE_EVENTS = 1 << 5,
  STABLE_FRAGMENT = 1 << 6,
  KEYED_FRAGMENT = 1 << 7,
  UNKEYED_FRAGMENT = 1 << 8,
  NEED_PATCH = 1 << 9,
  DYNAMIC_SLOTS = 1 << 10,
  HOISTED = -1,      // 静态节点
  BAIL = -2          // 差异太大需全量比较
}

// 编译后生成的代码
const _hoisted_1 = /*#__PURE__*/_createVNode("h1", null, "静态标题", -1 /* HOISTED */)

function render(_ctx) {
  return (_openBlock(), _createBlock("div", null, [
    _hoisted_1,
    _createVNode("p", null, _toDisplayString(_ctx.dynamicText), 1 /* TEXT */)
  ]))
}

优化效果

  1. 静态节点提升(HOISTED):跳过比较
  2. 动态标记(TEXT/PROPS):只比动态部分
  3. 更新性能提升:最高200%(官方Benchmark)

三、Composition API(开发范式革命)

3.1 Options API vs Composition API

Vue2 实现计数器

// src/components/Counter.vue
export default {
  data() {
    return { count: 0 }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  computed: {
    double() {
      return this.count * 2
    }
  }
}

Vue3 Composition API实现

// src/components/Counter.vue
import { ref, computed } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const double = computed(() => count.value * 2)
    
    function increment() {
      count.value++
    }
    
    return { count, double, increment }
  }
}

3.2 源码实现解析

核心源码packages/runtime-core/src/component.ts

// 组件实例创建流程
function setupComponent(instance) {
  // 解析Options API
  const { setup } = instance.type
  
  if (setup) {
    // 创建setup上下文
    const setupContext = createSetupContext(instance)
    
    // 执行setup函数
    const setupResult = callWithErrorHandling(
      setup, instance, [instance.props, setupContext]
    )
    
    // 处理返回结果
    if (isFunction(setupResult)) {
      // 返回渲染函数
      instance.render = setupResult
    } else if (isObject(setupResult)) {
      // 绑定到实例上下文
      instance.setupState = proxyRefs(setupResult)
    }
  }
  
  // 兼容Options API
  if (!instance.render) {
    instance.render = instance.type.render || NOOP
  }
}

设计优势

  1. 逻辑复用:自定义Hook代替Mixins

    // useCounter.js
    import { ref, computed } from 'vue'
    
    export function useCounter(initial = 0) {
      const count = ref(initial)
      const double = computed(() => count.value * 2)
      const increment = () => count.value++
      
      return { count, double, increment }
    }
    
  2. 更好的TS支持:完整类型推导

  3. 代码组织:相关逻辑集中管理


四、生命周期与API变化

4.1 生命周期映射

Vue2Vue3 Composition源码位置(Vue3)
beforeCreate使用setup()替代在createComponentInstance之前
created使用setup()替代在setupComponent之后
beforeMountonBeforeMountpackages/runtime-core/src/apiLifecycle.ts
mountedonMounted同上
beforeUpdateonBeforeUpdate同上
updatedonUpdated同上
beforeDestroyonBeforeUnmount同上
destroyedonUnmounted同上

4.2 全局API变化

Vue2 全局API

// src/core/global-api/index.js
export function initGlobalAPI(Vue) {
  Vue.config = {...}
  Vue.util = {...}
  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick
  Vue.observable = observable
  Vue.options = {}
  
  // 注册内置组件
  initUse(Vue)
  initMixin(Vue)
  initExtend(Vue)
  initAssetRegisters(Vue)
}

Vue3 模块化API

// packages/runtime-core/src/apiCreateApp.ts
export function createAppAPI(render) {
  return function createApp(rootComponent, rootProps = null) {
    const context = createAppContext()
    
    const app: App = {
      _component: rootComponent,
      _props: rootProps,
      _container: null,
      
      component(name, component) {
        context.components[name] = component
        return app
      },
      
      directive(name, directive) {
        context.directives[name] = directive
        return app
      },
      
      mount(rootContainer) {
        // 挂载逻辑
      }
    }
    
    return app
  }
}

变化核心

  • 全局API改为实例方法(避免污染)
  • 支持多应用实例(createApp()
  • Tree-shaking:未使用API不打包

五、模板编译优化(编译器升级)

5.1 Vue2 模板编译

编译产物

function render() {
  with(this) {
    return _c('div', [
      _c('h1', [_v("静态标题")]),
      _c('p', [_v(_s(dynamicText))])
    ])
  }
}

问题:无静态动态区分,全量Diff

5.2 Vue3 编译优化

源码位置packages/compiler-core/src/transform.ts

// PatchFlag 生成逻辑
export const transformElement = (node, context) => {
  // 标记动态属性
  const patchFlag = getPatchFlag(node)
  
  return () => {
    if (patchFlag !== PatchFlags.HOISTED) {
      node.codegenNode = createVNodeCall(
        context,
        node.tag,
        node.props,
        node.children,
        patchFlag
      )
    }
  }
}

// 编译产物(带PatchFlag)
import { createVNode as _createVNode, openBlock as _openBlock } from "vue"

export function render(_ctx) {
  return (_openBlock(),
  _createVNode("div", null, [
    _createVNode("h1", null, "静态标题"),
    _createVNode("p", null, _ctx.dynamicText, 1 /* TEXT */)
  ]))
}

核心优化点

  1. 静态节点提升(HOISTED):渲染函数外创建
  2. 静态属性提升:跳过属性比较
  3. PatchFlag标记:指导运行时Diff
  4. 事件缓存:避免重复创建

六、TypeScript支持(工程化升级)

6.1 Vue2 的TS困境

问题表现

import Vue from 'vue'

const Component = Vue.extend({
  props: {
    message: String // 类型声明有限
  },
  methods: {
    handleClick() {
      this.$emit('custom') // 无法推断事件类型
    }
  }
})

6.2 Vue3 全面TS重构

源码结构

packages/
  compiler-core/     # TS实现
  runtime-core/       # TS实现
  reactivity/         # TS实现
  ...

组件类型声明

// packages/runtime-core/src/apiDefineComponent.ts
export function defineComponent(options) {
  return isFunction(options)
    ? { setup: options }
    : options
}

// 使用示例
import { defineComponent } from 'vue'

export default defineComponent({
  props: {
    message: {
      type: String,
      required: true
    }
  },
  emits: ['change'], // 声明事件
  setup(props, { emit }) {
    props.message // 自动推断为string
    
    function handleChange() {
      emit('change') // 类型安全
    }
  }
})

优势

  1. 源码使用TS重写(覆盖率100%)
  2. 完善的类型定义(组件/API/插件)
  3. 模板类型检查(Volar插件支持)

总结:Vue3的全面进化

三大核心突破

  1. 性能飞跃
    • 响应式:Proxy替代defineProperty
    • 虚拟DOM:PatchFlag静态标记
    • 包体积:Tree-shaking减少41%体积
  2. 开发体验
    • Composition API逻辑复用
    • 更好的TypeScript支持
    • Fragment/Teleport等新特性
  3. 工程化
    • 模块化架构(@vue/runtime-core等)
    • 自定义渲染器(Canvas/Native支持)
    • 编译时优化(预编译静态节点)

Vue3 不是简单的版本迭代,而是前端框架设计的范式转变。其架构思想已影响 React 18、Svelte 等框架设计,代表了现代前端框架的发展方向。掌握 Vue3 不仅是为了使用新特性,更是理解前端框架设计思想的必修课。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值