Vue.js 是一个流行的前端 JavaScript 框架,用于构建用户界面和单页应用程序。Vue 3 引入了组合式 API(Composition API),这是一种新的方式来组织和重用逻辑代码。组合式 API 的核心是 setup() 函数和 reactive() 函数。本文将深入探讨这两个概念及其用法。
组合式 API 是 Vue 3 中引入的一种新的 API 风格,旨在解决 Vue 2 中选项式 API 的一些局限性。组合式 API 允许开发者将逻辑代码组织成可重用的函数,而不是将逻辑分散在不同的选项(如 data、methods、computed 等)中。
setup() 函数setup() 函数的概念setup() 函数是组合式 API 的核心入口点。它在组件实例创建之前执行,用于初始化组件的状态和逻辑。setup() 函数接收两个参数:
props:组件的 props 对象。context:包含一些常用的属性和方法,如 attrs、slots、emit 等。setup() 函数的返回值setup() 函数返回一个对象,该对象的属性和方法将被暴露给组件的模板和其他选项。这意味着你可以在模板中直接使用 setup() 函数中定义的变量和方法。
setup() 函数的基本用法import { ref } from 'vue';
export default {
setup(props, context) {
const count = ref(0);
function increment() {
count.value++;
}
return {
count,
increment,
};
},
};
在这个例子中,setup() 函数定义了一个响应式变量 count 和一个方法 increment,并将它们暴露给模板。
setup() 函数中的生命周期钩子在 setup() 函数中,你可以使用 Vue 3 提供的生命周期钩子函数来执行一些操作。这些钩子函数包括:
onBeforeMountonMountedonBeforeUpdateonUpdatedonBeforeUnmountonUnmountedimport { onMounted, onUnmounted } from 'vue';
export default {
setup() {
onMounted(() => {
console.log('Component is mounted!');
});
onUnmounted(() => {
console.log('Component is unmounted!');
});
},
};
setup() 函数中的 props 和 contextsetup() 函数的第一个参数是 props,它是一个响应式对象,包含了组件的 props。你可以直接访问 props 中的属性。
export default {
props: {
message: String,
},
setup(props) {
console.log(props.message);
},
};
setup() 函数的第二个参数是 context,它包含了一些常用的属性和方法:
attrs:包含了父组件传递的非 prop 属性。slots:包含了组件的插槽内容。emit:用于触发自定义事件。export default {
setup(props, context) {
console.log(context.attrs);
console.log(context.slots);
context.emit('custom-event');
},
};
reactive() 函数reactive() 函数的概念reactive() 函数是 Vue 3 中用于创建响应式对象的函数。它接收一个普通对象作为参数,并返回一个响应式代理对象。这个代理对象的所有属性都是响应式的,当属性发生变化时,Vue 会自动更新相关的视图。
reactive() 函数的基本用法import { reactive } from 'vue';
export default {
setup() {
const state = reactive({
count: 0,
message: 'Hello, Vue 3!',
});
function increment() {
state.count++;
}
return {
state,
increment,
};
},
};
在这个例子中,reactive() 函数创建了一个响应式对象 state,并将其暴露给模板。当 state.count 发生变化时,Vue 会自动更新相关的视图。
reactive() 函数与 ref() 函数的区别reactive() 函数和 ref() 函数都可以用于创建响应式数据,但它们有一些区别:
reactive() 函数用于创建响应式对象,而 ref() 函数用于创建响应式的基本类型值。reactive() 函数返回的是一个代理对象,而 ref() 函数返回的是一个包含 value 属性的对象。import { reactive, ref } from 'vue';
export default {
setup() {
const state = reactive({
count: 0,
});
const count = ref(0);
return {
state,
count,
};
},
};
在这个例子中,state 是一个响应式对象,而 count 是一个包含 value 属性的响应式对象。
reactive() 函数的嵌套使用reactive() 函数可以嵌套使用,以创建复杂的响应式对象。
import { reactive } from 'vue';
export default {
setup() {
const state = reactive({
user: {
name: 'John Doe',
age: 30,
},
settings: {
theme: 'dark',
notifications: true,
},
});
return {
state,
};
},
};
在这个例子中,state 是一个嵌套的响应式对象,包含了 user 和 settings 两个子对象。
reactive() 函数的局限性reactive() 函数有一些局限性:
import { reactive } from 'vue';
export default {
setup() {
const state = reactive({
count: 0,
});
// 这不会触发响应式更新
state = { count: 1 };
// 这会触发响应式更新
state.count = 1;
return {
state,
};
},
};
在这个例子中,直接替换 state 对象不会触发响应式更新,而修改 state.count 会触发响应式更新。
reactive() 和 ref() 管理响应式数据在组合式 API 中,reactive() 和 ref() 是管理响应式数据的主要工具。reactive() 适用于对象,而 ref() 适用于基本类型值。
import { reactive, ref } from 'vue';
export default {
setup() {
const state = reactive({
count: 0,
});
const message = ref('Hello, Vue 3!');
return {
state,
message,
};
},
};
在这个例子中,state 是一个响应式对象,而 message 是一个响应式的基本类型值。
computed() 创建计算属性computed() 函数用于创建计算属性。它接收一个函数作为参数,并返回一个响应式的计算属性。
import { reactive, computed } from 'vue';
export default {
setup() {
const state = reactive({
count: 0,
});
const doubleCount = computed(() => state.count * 2);
return {
state,
doubleCount,
};
},
};
在这个例子中,doubleCount 是一个计算属性,它的值始终是 state.count 的两倍。
watch() 监听响应式数据的变化watch() 函数用于监听响应式数据的变化。它接收两个参数:要监听的数据和一个回调函数。
import { reactive, watch } from 'vue';
export default {
setup() {
const state = reactive({
count: 0,
});
watch(() => state.count, (newValue, oldValue) => {
console.log(`count changed from ${oldValue} to ${newValue}`);
});
return {
state,
};
},
};
在这个例子中,watch() 函数监听 state.count 的变化,并在变化时执行回调函数。
watchEffect() 自动追踪依赖watchEffect() 函数用于自动追踪依赖,并在依赖发生变化时执行回调函数。
import { reactive, watchEffect } from 'vue';
export default {
setup() {
const state = reactive({
count: 0,
});
watchEffect(() => {
console.log(`count is ${state.count}`);
});
return {
state,
};
},
};
在这个例子中,watchEffect() 函数自动追踪 state.count 的变化,并在变化时执行回调函数。
组合式 API 允许开发者将逻辑代码封装在自定义 Hook 中,以便在多个组件中复用。
// useCounter.js
import { ref } from 'vue';
export function useCounter() {
const count = ref(0);
function increment() {
count.value++;
}
return {
count,
increment,
};
}
// MyComponent.vue
import { useCounter } from './useCounter';
export default {
setup() {
const { count, increment } = useCounter();
return {
count,
increment,
};
},
};
在这个例子中,useCounter 是一个自定义 Hook,它封装了计数器的逻辑,并在 MyComponent 中复用。
provide() 和 inject() 实现跨组件通信provide() 和 inject() 函数用于实现跨组件的依赖注入。
// ParentComponent.vue
import { provide, reactive } from 'vue';
export default {
setup() {
const state = reactive({
count: 0,
});
provide('state', state);
return {
state,
};
},
};
// ChildComponent.vue
import { inject } from 'vue';
export default {
setup() {
const state = inject('state');
return {
state,
};
},
};
在这个例子中,ParentComponent 使用 provide() 函数提供了一个响应式对象 state,而 ChildComponent 使用 inject() 函数注入了这个对象。
props在组合式 API 中,你可以使用 TypeScript 来定义 props 的类型。
import { defineComponent } from 'vue';
export default defineComponent({
props: {
message: {
type: String,
required: true,
},
},
setup(props) {
console.log(props.message);
},
});
在这个例子中,props.message 的类型被定义为 string,并且是必需的。
reactive() 和 ref() 的类型你可以使用 TypeScript 来定义 reactive() 和 ref() 的类型。
import { defineComponent, reactive, ref } from 'vue';
interface State {
count: number;
message: string;
}
export default defineComponent({
setup() {
const state = reactive<State>({
count: 0,
message: 'Hello, Vue 3!',
});
const count = ref<number>(0);
return {
state,
count,
};
},
});
在这个例子中,state 的类型被定义为 State 接口,而 count 的类型被定义为 number。
你可以使用 TypeScript 来定义自定义 Hook 的类型。
// useCounter.ts
import { ref } from 'vue';
export function useCounter() {
const count = ref<number>(0);
function increment() {
count.value++;
}
return {
count,
increment,
};
}
// MyComponent.vue
import { defineComponent } from 'vue';
import { useCounter } from './useCounter';
export default defineComponent({
setup() {
const { count, increment } = useCounter();
return {
count,
increment,
};
},
});
在这个例子中,useCounter 自定义 Hook 的类型被定义为返回一个包含 count 和 increment 的对象。
将逻辑代码封装在自定义 Hook 中,可以提高代码的可复用性和可维护性。
// useUser.js
import { reactive } from 'vue';
export function useUser() {
const user = reactive({
name: 'John Doe',
age: 30,
});
function updateName(newName) {
user.name = newName;
}
return {
user,
updateName,
};
}
// MyComponent.vue
import { useUser } from './useUser';
export default {
setup() {
const { user, updateName } = useUser();
return {
user,
updateName,
};
},
};
在这个例子中,useUser 自定义 Hook 封装了用户相关的逻辑,并在 MyComponent 中复用。
computed() 和 watch() 优化性能使用 computed() 和 watch() 可以优化组件的性能,避免不必要的重新渲染。
import { reactive, computed, watch } from 'vue';
export default {
setup() {
const state = reactive({
count: 0,
});
const doubleCount = computed(() => state.count * 2);
watch(() => state.count, (newValue, oldValue) => {
console.log(`count changed from ${oldValue} to ${newValue}`);
});
return {
state,
doubleCount,
};
},
};
在这个例子中,doubleCount 是一个计算属性,它的值只有在 state.count 发生变化时才会重新计算。watch() 函数监听 state.count 的变化,并在变化时执行回调函数。
provide() 和 inject() 实现跨组件通信使用 provide() 和 inject() 可以实现跨组件的依赖注入,避免 props 层层传递的繁琐。
// ParentComponent.vue
import { provide, reactive } from 'vue';
export default {
setup() {
const state = reactive({
count: 0,
});
provide('state', state);
return {
state,
};
},
};
// ChildComponent.vue
import { inject } from 'vue';
export default {
setup() {
const state = inject('state');
return {
state,
};
},
};
在这个例子中,ParentComponent 使用 provide() 函数提供了一个响应式对象 state,而 ChildComponent 使用 inject() 函数注入了这个对象。
setup() 函数中的 this 问题在 setup() 函数中,this 是 undefined,因此不能使用 this 来访问组件实例或选项。
export default {
setup() {
// 这会导致错误
console.log(this);
return {};
},
};
解决方案是使用 context 参数来访问组件的属性和方法。
export default {
setup(props, context) {
console.log(context.attrs);
console.log(context.slots);
context.emit('custom-event');
return {};
},
};
reactive() 函数中的嵌套对象问题reactive() 函数只能对对象进行响应式处理,如果对象中包含嵌套对象,需要手动处理。
import { reactive } from 'vue';
export default {
setup() {
const state = reactive({
user: {
name: 'John Doe',
age: 30,
},
});
// 这不会触发响应式更新
state.user = { name: 'Jane Doe', age: 25 };
// 这会触发响应式更新
state.user.name = 'Jane Doe';
state.user.age = 25;
return {
state,
};
},
};
解决方案是使用 reactive() 函数对嵌套对象进行处理。
import { reactive } from 'vue';
export default {
setup() {
const state = reactive({
user: reactive({
name: 'John Doe',
age: 30,
}),
});
// 这会触发响应式更新
state.user = reactive({ name: 'Jane Doe', age: 25 });
return {
state,
};
},
};
ref() 函数中的 .value 问题ref() 函数返回的是一个包含 .value 属性的对象,因此在访问和修改值时需要使用 .value。
import { ref } from 'vue';
export default {
setup() {
const count = ref(0);
// 这会触发响应式更新
count.value++;
return {
count,
};
},
};
解决方案是在模板中使用 ref 时不需要 .value,但在 JavaScript 代码中需要使用 .value。
”`javascript