一、Vue3 设计思想与 Vue2 差异对比
// 在 Vue2 中,通过 data 选项返回一个对象,对象中的属性会被 Object.defineProperty 转换为响应式数据
// 当这些属性的值发生变化时,Vue 会自动更新与之绑定的 DOM
export default {
data() {
return {
count: 0
};
}
};
// 在 Vue3 中,使用 reactive 函数将一个普通对象转换为响应式对象
// Proxy 可以拦截对象的各种操作,实现更强大的响应式功能
import { reactive } from 'vue';
const state = reactive({
count: 0
});
Object.defineProperty
对新增属性、删除属性和数组索引修改等操作无法自动追踪响应式变化。而 Vue3 的 Proxy
能拦截对象的属性访问、赋值、删除等操作,因此可以处理动态属性增删和数组索引修改等情况,并且在官方基准测试中性能有明显提升。// Vue2 的选项式 API 是将不同功能的代码分别放在不同的选项中
// data 选项用于定义数据,methods 选项用于定义方法,mounted 是生命周期钩子
export default {
data() {
return {
x: 0
};
},
methods: {
move() {
this.x++;
}
},
mounted() {
console.log('mounted');
}
};
// 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
};
}
};
二、Vue3 核心变化详解
import { ref, computed } from 'vue';
const count = ref(0);
// computed 用于创建计算属性,它会根据依赖的响应式数据自动更新
const double = computed(() => count.value * 2);
import { ref, watch } from 'vue';
const count = ref(0);
// watch 用于监听响应式数据的变化,当 count 变化时会执行回调函数
watch(count, (newVal, oldVal) => {
console.log(`count变化: ${oldVal} → ${newVal}`);
}, {
immediate: true
});
import { ref, watchEffect } from 'vue';
const count = ref(0);
// watchEffect 会立即执行一次回调函数,并自动追踪回调函数中使用的响应式数据
// 当这些数据变化时,回调函数会再次执行
const stop = watchEffect(() => {
console.log(`count值: ${count.value}`);
});
// 调用 stop 函数可以停止监听
stop();
// 选项式 API 按照不同的功能将代码组织在不同的选项中
export default {
data() {
return {
// 数据
};
},
methods: {
// 方法
},
computed: {
// 计算属性
},
// 生命周期钩子...
};
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
};
}
};
import { ref } from 'vue';
// ref 用于创建基本类型的响应式数据
// 访问 ref 的值需要通过 .value 属性
const count = ref(0);
console.log(count.value);
import { reactive } from 'vue';
// reactive 用于创建引用类型的响应式数据
// 访问 reactive 对象的属性可以直接访问
const state = reactive({
user: {
name: 'John'
},
items: ['apple', 'banana']
});
console.log(state.user.name);
export default {
props: {
title: String
},
setup(props, context) {
// props 是响应式的(不要解构)
console.log(props.title);
// context 包含 attrs/slots/emit
context.emit('submit');
// 返回模板可用的数据
return {
// ...
};
}
};
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')
}
]
});
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
};
}
};
async/await
语法,方便处理异步操作,如异步验证用户登录状态等。mutations
、actions
、getters
等概念,并且有严格的调用规则,API 相对复杂。而 Pinia 只需要使用 state
、actions
、getters
,API 更加简洁。import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++;
}
},
getters: {
double: (state) => state.count * 2
}
});
import { useCounterStore } from '@/stores/counter';
export default {
setup() {
const counter = useCounterStore();
return {
count: counter.count,
double: counter.double,
increment: counter.increment
};
}
};
五、组件通信方式
props
选项接收数据。// 子组件
import { defineEmits } from 'vue';
const emit = defineEmits(['update']);
// 触发自定义事件,并传递新值
emit('update', newValue);
// 父组件
// 祖先组件
import { provide } from 'vue';
// 提供一个名为 theme 的值
provide('theme', 'dark');
// 后代组件
import { inject } from 'vue';
// 注入 theme 值,如果没有提供则使用默认值 'light'
const theme = inject('theme', 'light');