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 提供的生命周期钩子函数来执行一些操作。这些钩子函数包括:
onBeforeMount
onMounted
onBeforeUpdate
onUpdated
onBeforeUnmount
onUnmounted
import { onMounted, onUnmounted } from 'vue';
export default {
setup() {
onMounted(() => {
console.log('Component is mounted!');
});
onUnmounted(() => {
console.log('Component is unmounted!');
});
},
};
setup()
函数中的 props
和 context
setup()
函数的第一个参数是 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