Skip to content

Vue 3 与 Vue 2 全面对比

1. 响应式系统

1.1 Vue 2 响应式原理

Vue 2 使用 Object.defineProperty 实现响应式系统。

javascript
function defineReactive(obj, key, val) {
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    get() {
      Dep.target && dep.addSub(Dep.target)
      return val
    },
    set(newVal) {
      if (newVal !== val) {
        val = newVal
        dep.notify()
      }
    }
  })
}

局限性

  • 无法监听对象新增属性
  • 无法监听数组索引和长度变化
  • 需要深度遍历对象,性能较差

1.2 Vue 3 响应式原理

Vue 3 使用 Proxy 实现响应式系统。

javascript
function reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      const result = Reflect.get(target, key, receiver)
      // 依赖收集
      track(target, key)
      // 递归处理嵌套对象
      if (isObject(result)) {
        return reactive(result)
      }
      return result
    },
    set(target, key, value, receiver) {
      const oldValue = Reflect.get(target, key, receiver)
      const result = Reflect.set(target, key, value, receiver)
      // 触发更新
      if (oldValue !== value) {
        trigger(target, key)
      }
      return result
    },
    deleteProperty(target, key) {
      const existed = Reflect.has(target, key)
      const result = Reflect.deleteProperty(target, key)
      if (existed) {
        trigger(target, key, 'delete')
      }
      return result
    }
  })
}

优势

  • 可以监听对象新增属性
  • 可以监听数组索引和长度变化
  • 不需要深度遍历,性能更好
  • 支持 Map、Set 等数据结构

2. 虚拟 DOM

2.1 Vue 2 虚拟 DOM

Vue 2 的虚拟 DOM 是基于 VNode 类实现的。

javascript
class VNode {
  constructor(tag, data, children, text, elm) {
    this.tag = tag
    this.data = data
    this.children = children
    this.text = text
    this.elm = elm
  }
}

特点

  • 每个 VNode 是一个对象,包含标签、数据、子节点等信息
  • 渲染函数返回 VNode 树
  • 通过 diff 算法比较新旧 VNode 树的差异

2.2 Vue 3 虚拟 DOM

Vue 3 对虚拟 DOM 进行了重构,使用了更高效的结构。

改进

  • 优化了 VNode 结构,减少了内存占用
  • 引入了 Fragment,支持多个根节点
  • 引入了 Teleport,支持组件内容的转移
  • 引入了 Suspense,支持异步组件的处理

3. Diff 算法

3.1 Vue 2 Diff 算法

Vue 2 使用双端比较算法。

javascript
function updateChildren(parentElm, oldCh, newCh) {
  let oldStartIdx = 0,
    newStartIdx = 0
  let oldEndIdx = oldCh.length - 1,
    newEndIdx = newCh.length - 1
  let oldStartVnode = oldCh[0],
    newStartVnode = newCh[0]
  let oldEndVnode = oldCh[oldEndIdx],
    newEndVnode = newCh[newEndIdx]
  let oldKeyToIdx, idxInOld, vnodeToMove, refElm

  // 双端比较逻辑
  while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
    // 省略比较逻辑...
  }

  // 处理剩余节点
  if (oldStartIdx > oldEndIdx) {
    // 添加新节点
  } else if (newStartIdx > newEndIdx) {
    // 删除旧节点
  }
}

局限性

  • 对于长列表,diff 算法的时间复杂度仍然较高
  • 无法利用静态节点的优势

3.2 Vue 3 Diff 算法

Vue 3 对 Diff 算法进行了优化,引入了静态提升、补丁标志等技术。

改进

  • 静态提升:将静态节点提升到渲染函数外部,避免重复创建
  • 补丁标志:为 VNode 添加补丁标志,只更新需要变化的部分
  • 最长递增子序列:使用最长递增子序列算法优化列表更新
  • 缓存事件处理函数:避免每次渲染都创建新的事件处理函数
javascript
// 编译后的代码示例
function render() {
  return (
    _openBlock(),
    _createElementBlock(
      _Fragment,
      null,
      [
        _createElementVNode('div', null, 'Hello'), // 静态节点
        _createElementVNode(
          'div',
          null,
          _toDisplayString(_ctx.count),
          1 /* TEXT */
        ) // 只更新文本
      ],
      64 /* STABLE_FRAGMENT */
    )
  )
}

4. 编译优化

4.1 Vue 2 编译

Vue 2 的编译过程包括:

  • 模板解析:将模板解析为 AST
  • 优化:标记静态节点
  • 代码生成:生成渲染函数

局限性

  • 编译优化有限,运行时仍需进行较多计算
  • 无法充分利用静态节点的优势

4.2 Vue 3 编译优化

Vue 3 引入了更先进的编译优化技术:

编译时优化

  • 静态提升:将静态节点和静态属性提升到渲染函数外部
  • 补丁标志:为动态节点添加补丁标志,只更新需要变化的部分
  • 缓存事件处理函数:缓存事件处理函数,避免每次渲染都创建新函数
  • 内联模板:对于简单模板,直接内联渲染逻辑

运行时优化

  • 块树:将模板分割为多个块,只更新变化的块
  • 动态指令参数:优化动态指令参数的处理
  • v-for 优化:优化 v-for 指令的处理

5. 组合式 API vs 选项式 API

5.1 Vue 2 选项式 API

Vue 2 使用选项式 API,将代码按功能分为 data、methods、computed 等选项。

javascript
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  computed: {
    doubleCount() {
      return this.count * 2
    }
  },
  watch: {
    count(newVal) {
      console.log('Count changed:', newVal)
    }
  }
}

局限性

  • 代码组织按功能划分,逻辑分散
  • 难以复用逻辑
  • 类型推导困难

5.2 Vue 3 组合式 API

Vue 3 引入了组合式 API,允许按逻辑关注点组织代码。

javascript
import { ref, computed, watch } from 'vue'

export default {
  setup() {
    const count = ref(0)

    const increment = () => {
      count.value++
    }

    const doubleCount = computed(() => count.value * 2)

    watch(count, newVal => {
      console.log('Count changed:', newVal)
    })

    return {
      count,
      increment,
      doubleCount
    }
  }
}

优势

  • 按逻辑关注点组织代码,逻辑集中
  • 易于复用逻辑
  • 更好的类型推导
  • 更灵活的代码组织

6. 性能优化

6.1 Vue 2 性能优化

Vue 2 的性能优化主要依赖于:

  • 使用 v-if 和 v-show 合理切换
  • 使用 key 提高列表渲染性能
  • 使用 computed 缓存计算结果
  • 使用 watch 监听变化
  • 合理使用 keep-alive

6.2 Vue 3 性能优化

Vue 3 在性能方面进行了全面优化:

编译时优化

  • 静态提升
  • 补丁标志
  • 缓存事件处理函数

运行时优化

  • 响应式系统优化(使用 Proxy)
  • Diff 算法优化
  • 块树优化
  • 内存使用优化

具体数据

  • 渲染性能提升 1.3-2 倍
  • 内存使用减少 50%
  • 打包体积减少 10-20%

7. TypeScript 支持

7.1 Vue 2 TypeScript 支持

Vue 2 的 TypeScript 支持有限:

  • 需要手动添加类型定义
  • 类型推导不友好
  • 组件选项类型定义复杂

7.2 Vue 3 TypeScript 支持

Vue 3 内置了 TypeScript 支持:

  • 完全重写了类型定义
  • 更好的类型推导
  • 组合式 API 天然支持 TypeScript
  • 模板中的类型检查
typescript
// Vue 3 TypeScript 示例
import { defineComponent, ref, computed } from 'vue'

export default defineComponent({
  setup() {
    const count = ref<number>(0)
    const doubleCount = computed<number>(() => count.value * 2)

    const increment = (): void => {
      count.value++
    }

    return {
      count,
      doubleCount,
      increment
    }
  }
})

8. 生命周期

8.1 Vue 2 生命周期

Vue 2 的生命周期钩子:

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyed
  • activated (keep-alive 专用)
  • deactivated (keep-alive 专用)

8.2 Vue 3 生命周期

Vue 3 的生命周期钩子:

  • setup (替代 beforeCreate 和 created)
  • onBeforeMount (替代 beforeMount)
  • onMounted (替代 mounted)
  • onBeforeUpdate (替代 beforeUpdate)
  • onUpdated (替代 updated)
  • onBeforeUnmount (替代 beforeDestroy)
  • onUnmounted (替代 destroyed)
  • onActivated (替代 activated)
  • onDeactivated (替代 deactivated)
  • onErrorCaptured (替代 errorCaptured)
  • onRenderTracked
  • onRenderTriggered

使用方式

javascript
import { onMounted, onUpdated, onUnmounted } from 'vue'

export default {
  setup() {
    onMounted(() => {
      console.log('Component mounted')
    })

    onUpdated(() => {
      console.log('Component updated')
    })

    onUnmounted(() => {
      console.log('Component unmounted')
    })
  }
}

9. 其他新特性

9.1 Vue 3 新特性

  1. Fragment:支持多个根节点
vue
<template>
  <div>First root</div>
  <div>Second root</div>
</template>
  1. Teleport:支持组件内容的转移
vue
<template>
  <Teleport to="#modal">
    <div class="modal">Modal content</div>
  </Teleport>
</template>
  1. Suspense:支持异步组件的处理
vue
<template>
  <Suspense>
    <template #default>
      <AsyncComponent />
    </template>
    <template #fallback>
      <div>Loading...</div>
    </template>
  </Suspense>
</template>
  1. Composition API:组合式 API
  2. 更好的 TypeScript 支持
  3. 新的响应式 API:ref、reactive、computed、watch 等
  4. 新的编译优化:静态提升、补丁标志等
  5. 更小的打包体积

10. 迁移策略

10.1 从 Vue 2 迁移到 Vue 3

步骤

  1. 升级 Vue 3 和相关依赖
  2. 替换 Vue 2 的 API 为 Vue 3 的 API
  3. 重构选项式 API 为组合式 API(可选)
  4. 修复兼容性问题

工具

  • Vue Migration Build:提供兼容层,允许渐进式迁移
  • vue-codemod:自动转换代码

10.2 兼容性问题

需要注意的兼容性问题

  • 全局 API 变更:Vue 3 中的全局 API 被移到了 createApp 返回的实例上
  • 过滤器移除:Vue 3 移除了过滤器,建议使用计算属性或方法替代
  • 指令钩子变更:Vue 3 中的指令钩子发生了变化
  • 事件 API 变更:Vue 3 中的事件 API 发生了变化

11. 代码示例对比

11.1 响应式系统对比

Vue 2

javascript
// Vue 2 响应式
const vm = new Vue({
  data: {
    user: {
      name: 'John',
      age: 30
    },
    items: [1, 2, 3]
  }
})

// 无法监听新增属性
vm.user.email = 'john@example.com' // 不会触发更新

// 无法监听数组索引变化
vm.items[0] = 10 // 不会触发更新

// 无法监听数组长度变化
vm.items.length = 0 // 不会触发更新

Vue 3

javascript
// Vue 3 响应式
import { reactive } from 'vue'

const state = reactive({
  user: {
    name: 'John',
    age: 30
  },
  items: [1, 2, 3]
})

// 可以监听新增属性
state.user.email = 'john@example.com' // 会触发更新

// 可以监听数组索引变化
state.items[0] = 10 // 会触发更新

// 可以监听数组长度变化
state.items.length = 0 // 会触发更新

11.2 组件定义对比

Vue 2

javascript
// Vue 2 组件
export default {
  props: {
    title: String
  },
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  computed: {
    doubleCount() {
      return this.count * 2
    }
  },
  watch: {
    count(newVal) {
      console.log('Count changed:', newVal)
    }
  },
  mounted() {
    console.log('Component mounted')
  }
}

Vue 3

javascript
// Vue 3 组件
import { ref, computed, watch, onMounted } from 'vue';

export default {
  props: {
    title: String
  },
  setup(props) {
    const count = ref(0);

    const increment = () => {
      count.value++;
    };

    const doubleCount = computed(() => count.value * 2);

    watch(count, (newVal) => {
      console.log('Count changed:', newVal);
    });

    onMounted(() => {
      console.log('Component mounted');
    });

    return {
      count,
      increment,
      doubleCount
    };
  }
};

// 或使用 <script setup>
<script setup>
import { ref, computed, watch, onMounted } from 'vue';

const props = defineProps({
  title: String
});

const count = ref(0);

const increment = () => {
  count.value++;
};

const doubleCount = computed(() => count.value * 2);

watch(count, (newVal) => {
  console.log('Count changed:', newVal);
});

onMounted(() => {
  console.log('Component mounted');
});
</script>

12. 性能对比

12.1 渲染性能

场景Vue 2Vue 3提升
首次渲染100ms50ms50%
更新渲染80ms30ms62.5%
内存使用100MB50MB50%

12.2 打包体积

场景Vue 2Vue 3减少
基础库20KB10KB50%
完整库30KB20KB33.3%

13. 适用场景

13.1 Vue 2 适用场景

  • 现有项目,不需要立即升级
  • 对浏览器兼容性要求较高(需要支持 IE11)
  • 团队对 Vue 2 更熟悉

13.2 Vue 3 适用场景

  • 新项目,特别是大型项目
  • 需要更好的 TypeScript 支持
  • 需要更好的性能
  • 需要使用组合式 API 组织代码
  • 不需要支持 IE11

14. 总结

Vue 3 是 Vue 2 的重大升级,带来了许多改进和新特性:

  • 响应式系统:使用 Proxy 替代 Object.defineProperty,解决了 Vue 2 响应式系统的局限性
  • 虚拟 DOM:重构了虚拟 DOM,引入了 Fragment、Teleport、Suspense 等新特性
  • Diff 算法:优化了 Diff 算法,引入了静态提升、补丁标志等技术
  • 编译优化:引入了更先进的编译优化技术,提高了运行时性能
  • 组合式 API:引入了组合式 API,提供了更好的代码组织方式
  • TypeScript 支持:内置了更好的 TypeScript 支持
  • 性能优化:全面优化了性能,包括渲染性能、内存使用、打包体积等

Vue 3 的设计理念更加现代化,提供了更好的开发体验和性能表现。对于新的项目,Vue 3 是一个更好的选择。对于现有的 Vue 2 项目,可以根据实际情况考虑渐进式迁移到 Vue 3。

总之,Vue 3 是 Vue 框架的一个重要里程碑,它为前端开发带来了更高效、更灵活、更现代化的解决方案。

基于 VitePress 的本地知识库