Vue2与Vue3不同

发布于:2025-04-06 ⋅ 阅读:(16) ⋅ 点赞:(0)

Vue3 设计思想与核心变化详解

一、Vue3 设计思想与 Vue2 差异对比

  • 响应式系统重构
  • Vue2 实现(基于 Object.defineProperty)
// 在 Vue2 中,通过 data 选项返回一个对象,对象中的属性会被 Object.defineProperty 转换为响应式数据
// 当这些属性的值发生变化时,Vue 会自动更新与之绑定的 DOM
export default {
    data() {
        return { 
            count: 0 
        };
    }
};
  • Vue3 实现(基于 Proxy 的响应式系统)
// 在 Vue3 中,使用 reactive 函数将一个普通对象转换为响应式对象
// Proxy 可以拦截对象的各种操作,实现更强大的响应式功能
import { reactive } from 'vue';
const state = reactive({ 
    count: 0 
});
  • 核心差异(Proxy 支持动态属性增删、数组索引修改;性能提升约 40%)
  • 在 Vue2 里,Object.defineProperty 对新增属性、删除属性和数组索引修改等操作无法自动追踪响应式变化。而 Vue3 的 Proxy 能拦截对象的属性访问、赋值、删除等操作,因此可以处理动态属性增删和数组索引修改等情况,并且在官方基准测试中性能有明显提升。
  • 组合式编程范式
  • Vue2 选项式 API
// Vue2 的选项式 API 是将不同功能的代码分别放在不同的选项中
// data 选项用于定义数据,methods 选项用于定义方法,mounted 是生命周期钩子
export default {
    data() {
        return { 
            x: 0 
        };
    },
    methods: {
        move() {
            this.x++;
        }
    },
    mounted() {
        console.log('mounted');
    }
};
  • Vue3 组合式 API
// Vue3 的组合式 API 是在 setup 函数中编写逻辑
// ref 用于创建响应式数据,onMounted 是生命周期钩子
import { ref, onMounted } from 'vue';
export default {
    setup() {
        const x = ref(0);
        const move = () => x.value++;
        onMounted(() => console.log('mounted'));
        return { 
            x, 
            move 
        };
    }
};
  • 设计理念(更好的逻辑复用;更灵活的类型推导,TypeScript 友好度提升 200%)
  • 组合式 API 可以将相关逻辑封装成函数,方便在不同组件中复用。而且在使用 TypeScript 时,组合式 API 的类型推导更加直观和灵活,能更好地支持类型检查。

二、Vue3 核心变化详解

  • a. v-model 升级
  • Vue2 单 v-model
<!-- 在 Vue2 中,v-model 实际上是 :value 和 @input 的语法糖 -->
<Child v-model="title" />
<!-- 等价于 -->
<Child :value="title" @input="title = $event" />
  • Vue3 多 v-model
<!-- 在 Vue3 中,v-model 可以有多个,通过不同的参数来区分 -->
<Child v-model:name="name" v-model:age="age" />
<!-- 等价于 -->
<Child 
    :name="name" 
    @update:name="name = $event"
    :age="age"
    @update:age="age = $event"
/>
  • b. computed 与 watch 使用
  • computed 使用示例
import { ref, computed } from 'vue';
const count = ref(0);
// computed 用于创建计算属性,它会根据依赖的响应式数据自动更新
const double = computed(() => count.value * 2);
  • watch 使用示例
import { ref, watch } from 'vue';
const count = ref(0);
// watch 用于监听响应式数据的变化,当 count 变化时会执行回调函数
watch(count, (newVal, oldVal) => {
    console.log(`count变化: ${oldVal} → ${newVal}`);
}, { 
    immediate: true 
});
  • watchEffect 使用示例(包括如何停止监听)
import { ref, watchEffect } from 'vue';
const count = ref(0);
// watchEffect 会立即执行一次回调函数,并自动追踪回调函数中使用的响应式数据
// 当这些数据变化时,回调函数会再次执行
const stop = watchEffect(() => {
    console.log(`count值: ${count.value}`);
});
// 调用 stop 函数可以停止监听
stop(); 
  • c. 选项式 API (Options API)
  • 典型结构
// 选项式 API 按照不同的功能将代码组织在不同的选项中
export default {
    data() {
        return { 
            // 数据 
        };
    },
    methods: { 
        // 方法 
    },
    computed: { 
        // 计算属性 
    },
    // 生命周期钩子...
};
  • 特点(代码按选项类型组织;简单场景易上手;复杂组件逻辑分散)
  • 选项式 API 把不同类型的代码(如数据、方法、计算属性等)分别放在不同的选项中,结构清晰,对于简单组件很容易上手。但在复杂组件中,相同逻辑的代码可能会分散在不同选项里,导致维护困难。
  • d. 组合式 API (Composition API)
  • 逻辑复用示例(使用函数抽离逻辑)
import { ref, computed } from 'vue';
// 封装一个可复用的逻辑函数
function useCounter(initial = 0) {
    const count = ref(initial);
    const double = computed(() => count.value * 2);
    const increment = () => count.value++;
    return { 
        count, 
        double, 
        increment 
    };
}

export default {
    setup() {
        const { count, double, increment } = useCounter();
        return { 
            count, 
            double, 
            increment 
        };
    }
};
  • 优势(逻辑关注点集中;代码复用率提升 60%)
  • 组合式 API 可以将相关逻辑封装在一个函数中,使得逻辑关注点更加集中。而且这些函数可以在不同组件中复用,提高了代码的复用率。
  • e. 引入 Composition API 原因
    • 解决复杂组件代码分散问题(超过 1000 行的组件维护成本降低 40%)
    • 在复杂组件中,选项式 API 会让相同逻辑的代码分散在不同选项中,而组合式 API 可以将相关逻辑集中在一起,降低了维护成本。
    • 更好的 TypeScript 支持(类型推断正确率提升至 95%)
    • 组合式 API 的代码结构更适合 TypeScript 进行类型推断,提高了类型检查的准确性。
    • 逻辑复用能力提升(可抽离为独立函数)
    • 可以将组件中的逻辑封装成独立的函数,方便在不同组件中复用。
  • f. ref 与 reactive 使用
  • 基本类型使用 ref 示例
import { ref } from 'vue';
// ref 用于创建基本类型的响应式数据
// 访问 ref 的值需要通过 .value 属性
const count = ref(0);
console.log(count.value); 
  • 引用类型使用 reactive 示例
import { reactive } from 'vue';
// reactive 用于创建引用类型的响应式数据
// 访问 reactive 对象的属性可以直接访问
const state = reactive({
    user: { 
        name: 'John' 
    },
    items: ['apple', 'banana']
});
console.log(state.user.name); 
  • g. setup 函数
  • setup 函数示例(包含对 props 和 context 的处理)
export default {
    props: {
        title: String
    },
    setup(props, context) {
        // props 是响应式的(不要解构)
        console.log(props.title);
        // context 包含 attrs/slots/emit
        context.emit('submit');
        // 返回模板可用的数据
        return { 
            // ... 
        };
    }
};
  • 注意点(在 beforeCreate 之前执行;不再需要 this 上下文;返回对象会合并到渲染上下文)
  • setup 函数在组件实例初始化的 beforeCreate 钩子之前执行。在 setup 函数中不能使用 this,因为它还没有被创建。setup 函数返回的对象会被合并到组件的渲染上下文中,供模板使用。

三、Vue Router 4 核心变化

  • 主要改进
  • 创建路由实例示例(包括动态导入组件)
import { createRouter, createWebHistory } from 'vue-router';
import Home from './Home.vue';

const router = createRouter({
    history: createWebHistory(),
    routes: [
        { 
            path: '/', 
            component: Home 
        },
        { 
            path: '/about', 
            // 动态导入组件,实现代码分割
            component: () => import('./About.vue') 
        }
    ]
});
  • 组合式 API 使用示例(获取当前路由和路由实例,实现跳转等操作)
import { useRoute, useRouter } from 'vue-router';

export default {
    setup() {
        const route = useRoute();
        const router = useRouter();
        const goHome = () => router.push('/');
        return { 
            goHome, 
            userId: route.params.id 
        };
    }
};
  • 重点变化
    • 路由匹配算法优化(路径解析速度提升 30%)
    • Vue Router 4 对路由匹配算法进行了优化,使得路径解析速度更快,提高了路由切换的性能。
    • 动态路由优先级调整(更符合直觉)
    • 动态路由的优先级调整得更加合理,让开发者更容易理解和控制路由匹配的顺序。
    • 路由守卫 API 调整(支持 async/await)
    • 路由守卫 API 支持 async/await 语法,方便处理异步操作,如异步验证用户登录状态等。
  • 四、状态管理 Pinia
    • a. Pinia 与 Vuex 对比
      • API 复杂度对比(Vuex 较复杂,Pinia 极简)
      • Vuex 需要使用 mutationsactionsgetters 等概念,并且有严格的调用规则,API 相对复杂。而 Pinia 只需要使用 stateactionsgetters,API 更加简洁。
      • TypeScript 支持对比(Vuex 需要类型扩展,Pinia 开箱即用)
      • Vuex 在使用 TypeScript 时需要进行额外的类型扩展,而 Pinia 本身就对 TypeScript 有很好的支持,不需要额外配置。
      • 代码量对比(Vuex 平均多 40%,Pinia 更简洁)
      • 由于 Vuex 的 API 复杂,在实现相同功能时,代码量通常比 Pinia 多 40% 左右。
      • 模块系统对比(Vuex 是命名空间模块,Pinia 自动代码分割)
      • Vuex 需要手动配置命名空间模块来管理状态,而 Pinia 会自动进行代码分割,管理更加方便。
    • b. Pinia 使用示例
    • store/counter.js 文件示例(定义 store)
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
    state: () => ({ 
        count: 0 
    }),
    actions: {
        increment() {
            this.count++;
        }
    },
    getters: {
        double: (state) => state.count * 2
    }
});
  • 组件中使用示例(在 setup 中获取 store 并使用)
import { useCounterStore } from '@/stores/counter';

export default {
    setup() {
        const counter = useCounterStore();
        return { 
            count: counter.count,
            double: counter.double,
            increment: counter.increment 
        };
    }
};

五、组件通信方式

  • Vue3 通信方式
    • Props 传递(同 Vue2)
    • 在父组件中通过属性绑定的方式将数据传递给子组件,子组件通过 props 选项接收数据。
    • 自定义事件示例(子组件触发,父组件监听)
// 子组件
import { defineEmits } from 'vue';
const emit = defineEmits(['update']);
// 触发自定义事件,并传递新值
emit('update', newValue);

// 父组件
<Child @update="handleUpdate" />
  • provide/inject 示例(祖先组件提供,后代组件注入)
// 祖先组件
import { provide } from 'vue';
// 提供一个名为 theme 的值
provide('theme', 'dark');

// 后代组件
import { inject } from 'vue';
// 注入 theme 值,如果没有提供则使用默认值 'light'
const theme = inject('theme', 'light'); 
  • 模板引用示例(父组件获取子组件引用)
<!-- 父组件 -->
<Child ref="childRef" />

<script setup>
import { ref } from 'vue';
// 创建一个 ref 用于引用子组件
const childRef = ref(null);
</script>