Code前端首页关于Code前端联系我们

Vue2和Vue3有什么区别?新项目该选哪个?

terry 2小时前 阅读数 5 #Vue
文章标签 Vue2 Vue3

不少刚开始学Vue的朋友,或者要接手项目的开发者,总会纠结:Vue2和Vue3差别到底有多大?新项目选哪个更合适?老项目要不要急着迁移?今天咱们从核心原理、开发体验、生态兼容这些角度拆解,帮你选对方向。

核心原理差异:响应式、API设计、性能的迭代

响应式:从“Object.defineProperty”到“Proxy”的质变

Vue2靠Object.defineProperty实现响应式:遍历对象的每个属性,给属性加上gettersetter,拦截数据的读取和修改,但这种方式有明显缺陷:

  • 对象新增/删除属性监听不到:比如给对象obj新增属性obj.newProp = 'xxx',Vue2没法检测到变化,页面不会更新,得用this.$set(obj, 'newProp', 'xxx')手动触发。
  • 数组特殊处理不彻底:Vue2对数组的push/pop等方法做了原型重写,但像arr[0] = 1(直接改索引)、arr.length = 0(改长度)这类操作,还是监听不到,得用this.$set(arr, 0, 1)

Vue3改用Proxy代理整个对象:通过拦截对象的get(读取属性)、set(修改属性)等行为,实现更全面的响应式,优势很直观:

  • 对象任意操作都能监听:新增属性obj.newProp = 'xxx'、删除属性delete obj.oldProp,Proxy都能检测到,不用手动调用$set/$delete
  • 数组操作全拦截:不管是arr[0] = 1还是arr.length = 0,Proxy都能捕获到变化,数组响应式终于“完整”了。
  • 性能更优:Vue2要递归遍历对象的所有属性(哪怕深层属性暂时用不到),Proxy则是“懒拦截”,用到深层属性时再处理,初始化响应式数据的速度更快。

举个伪代码对比更直观:
Vue2响应式逻辑(简化版):

function observe(obj) {
  if (typeof obj !== 'object' || obj === null) return;
  Object.keys(obj).forEach(key => {
    let val = obj[key];
    observe(val); // 递归处理深层对象
    Object.defineProperty(obj, key, {
      get() { return val; },
      set(newVal) {
        if (newVal !== val) {
          val = newVal;
          // 触发视图更新
        }
      }
    });
  });
}

Vue3响应式逻辑(简化版):

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      // 收集依赖(比如哪个组件用了这个属性)
      track(target, key);
      return target[key];
    },
    set(target, key, newVal) {
      // 触发更新(通知依赖的组件重新渲染)
      trigger(target, key, newVal);
      return true;
    }
  });
}

代码组织:Options API到Composition API的逻辑革命

Vue2用Options API组织代码:把组件逻辑拆成datamethodscomputedwatch等选项,简单组件写起来顺手,但复杂组件就暴露问题了——逻辑分散

比如一个处理“用户信息加载+表单提交+定时器刷新”的组件,Vue2代码会变成这样:

export default {
  data() { return { user: {}, timer: null, form: { name: '' } } },
  created() { this.fetchUser(); this.timer = setInterval(this.fetchUser, 5000) },
  destroyed() { clearInterval(this.timer) },
  methods: { 
    async fetchUser() { this.user = await api.getUser() }, 
    async submitForm() { await api.submit(this.form); this.fetchUser() } 
  },
  watch: { 'form.name'(newVal) { /* 验证逻辑 */ } }
}

要找“用户信息相关逻辑”,得在datacreateddestroyedmethodswatch里跳来跳去,像拼碎片一样。

Vue3的Composition API(配合<script setup>语法糖)则是按功能聚合逻辑,可以把同一功能的代码封装成“组合函数”,复用性和可维护性拉满,还是上面的例子,用Vue3改写:

// 抽离用户信息逻辑到 useUser.js
import { ref, onMounted, onUnmounted } from 'vue';
import { api } from './api';
export function useUser() {
  const user = ref({});
  const timer = ref(null);
  const fetchUser = async () => {
    user.value = await api.getUser();
  };
  onMounted(() => {
    fetchUser();
    timer.value = setInterval(fetchUser, 5000);
  });
  onUnmounted(() => {
    clearInterval(timer.value);
  });
  return { user, fetchUser };
}
// 抽离表单逻辑到 useForm.js
import { ref, watch } from 'vue';
import { api } from './api';
export function useForm() {
  const form = ref({ name: '' });
  const submitForm = async () => {
    await api.submit(form.value);
  };
  watch(() => form.value.name, (newVal) => {
    // 名称验证逻辑
  });
  return { form, submitForm };
}
// 组件中按需引入,逻辑清晰
<script setup>
import { useUser } from './useUser.js';
import { useForm } from './useForm.js';
const { user, fetchUser } = useUser();
const { form, submitForm } = useForm();
// 提交后刷新用户信息
const handleSubmit = async () => {
  await submitForm();
  fetchUser();
};
</script>
<template>
  <div>{{ user.name }}</div>
  <input v-model="form.name" />
  <button @click="handleSubmit">提交</button>
</template>

可以看到,“用户信息”和“表单”的逻辑被分别封装到useUseruseForm里,组件只负责“组装”,后续维护时,找功能相关代码直接看对应的组合函数,不用在多个选项里翻找;如果其他组件需要类似逻辑,直接导入组合函数复用即可。

性能优化:编译和打包的双重升级

Vue3在编译阶段打包阶段都做了优化:

  • 编译优化(静态标记+PatchFlags)
    Vue2的模板Diff是“全量对比”,不管节点是否变化,都要逐个比对,Vue3的编译器会给模板做静态标记:把节点分成“静态节点”(比如纯文本、不变的元素)和“动态节点”(比如带v-bindv-if的元素),更新时,静态节点直接跳过,动态节点用PatchFlags标记更新类型(比如只更新文本、只更新class),实现“精准Diff”,减少不必要的计算。

  • 打包优化(Tree-shaking)
    Vue2的代码是“单例导出”,哪怕只用到一个API,也会打包整个库,Vue3改用“按需导出”,比如import { ref, computed } from 'vue',打包工具会自动剔除没用到的API(像Vue2里的filter,Vue3已经移除,改用computed或方法代替),最终Vue3核心库的体积比Vue2小了近一半,页面加载更快。

开发体验:语法糖、工具链带来的效率飞跃

语法糖让代码“瘦身”: