Vue 3 Composition API 深度研究

1. 核心概念与用法

Vue 3 的 Composition API 引入了一套全新的、基于函数的 API,旨在解决在构建大型复杂应用时,Options API 所面临的代码组织困难和逻辑复用性差的痛点。它并非要完全取代 Options API,而是提供了一种更灵活、更强大的替代方案,让开发者能够根据逻辑关注点来组织代码,而不是被限制在 datamethodscomputed 等固定的选项中 。这套 API 的核心思想是将组件的逻辑拆分成更小、更独立的函数,这些函数可以根据需要自由组合,从而极大地提升了代码的可读性、可维护性和复用性 。Composition API 主要包含响应式 API(如 refreactive)、生命周期钩子(如 onMounted)以及依赖注入(如 provideinject)等,它们共同构成了一个功能强大的工具集,使开发者能够以更函数式、更模块化的方式来构建 Vue 组件 。

1.1 setup() 函数:Composition API 的入口

setup() 函数是 Composition API 的核心和入口点。它在组件实例被创建之前执行,是组织组件逻辑的主要场所。与 Options API 中逻辑分散在各个选项(data, methods, computed 等)不同,setup() 允许我们将相关的状态、方法、计算属性等逻辑集中在一起,按照功能模块进行组织,从而解决了复杂组件中逻辑分散、难以追踪的问题 。这个函数接收两个参数:propscontextprops 是一个响应式对象,包含了父组件传递过来的所有属性,当父组件的 props 发生变化时,setup 函数会重新执行以获取最新的值 。context 是一个普通的 JavaScript 对象,它暴露了 attrsslotsemit 等组件上下文信息,这些在 Vue 2 中通过 this 访问的属性,在 setup 中需要通过 context 来访问,因为 setup 执行时组件实例尚未创建,this 并不可用 。

setup() 函数的返回值决定了模板中可以访问的变量和方法。任何在 setup 中定义并返回的对象、函数或响应式数据,都可以直接在组件的模板中使用。例如,我们可以返回一个包含响应式数据和方法的对象,模板就可以通过插值语法或事件绑定来直接使用它们 。这种显式的返回机制使得组件的 API 更加清晰,开发者可以一目了然地知道哪些状态和行为是暴露给模板使用的。此外,Vue 3 还引入了 <script setup> 语法糖,它简化了 setup 函数的写法,使得在单文件组件中使用 Composition API 变得更加简洁和直观。在 <script setup> 中,所有顶层定义的变量和函数都会自动暴露给模板,无需手动返回,进一步提升了开发效率 。

1.1.1 setup() 的执行时机与参数

setup() 函数的执行时机非常早,它在组件生命周期的 beforeCreate 钩子之前被调用,此时组件实例尚未完全创建,因此在 setup 内部无法访问 this 上下文 。这个函数接收两个主要参数:propscontext,并返回一个对象,该对象中的属性和方法将被暴露给组件的模板(template)使用 。

  1. props: 这是一个响应式的对象,包含了父组件传递给当前组件的所有属性。与 Vue 2 不同,这里的 props 是只读的,直接修改它不会触发更新,并且会在开发模式下收到警告。由于 props 是响应式的,你可以使用 watchwatchEffect 来监听其变化 。

  2. context: 这是一个普通的 JavaScript 对象,暴露了三个组件的 property:

    • attrs: 包含了所有未在 props 中声明的属性(即非 prop 的 attribute)。与 props 不同,attrs 不是响应式的。
    • slots: 一个插槽函数的对象,可以用来访问插槽内容。
    • emit: 一个函数,用于触发自定义事件,向父组件传递数据。它等价于 Options API 中的 this.$emit

一个完整的 setup 函数签名示例如下:

import {
   
    defineComponent } from 'vue';

export default defineComponent({
   
   
  props: {
   
   
    message: String
  },
  setup(props, {
    
     attrs, slots, emit }) {
   
   
    console.log('Props:', props.message);
    console.log('Attrs:', attrs);
    console.log('Slots:', slots);
    console.log('Emit function:', emit);
    
    // ... 定义响应式数据、方法等
    
    return {
   
   
      // 返回要暴露给模板的属性和方法
    };
  }
});

通过这种方式,setup 函数为组件的逻辑提供了一个清晰、独立的入口点,使得代码组织更加模块化 。

1.1.2 setup() 的返回值与模板访问

setup() 函数必须返回一个对象,这个返回的对象中的属性(包括响应式数据、计算属性、方法等)将会被合并到组件的渲染上下文中,从而可以在模板中直接使用。这是连接 Composition API 逻辑与组件视图的桥梁 。例如,如果你在 setup 中定义了一个 ref 变量 count 和一个方法 increment,你需要在返回的对象中显式地包含它们,模板才能访问 { { count }}@click="increment"

除了返回一个对象,setup 函数还可以返回一个渲染函数(render function)。当返回渲染函数时,该函数将直接用于渲染组件的 DOM,此时不能再返回其他属性。这种方式提供了对渲染过程的完全控制,适用于需要高度动态渲染的场景。如果在这种情况下仍然需要向父组件暴露一些方法(例如,通过模板 ref 调用),可以使用 context 参数中的 expose 函数。expose 接收一个对象,该对象中定义的属性将被显式地暴露给组件实例,即使 setup 返回的是一个渲染函数 。

import {
   
    h, ref } from 'vue';

export default {
   
   
  setup(props, {
    
     expose }) {
   
   
    const count = ref(0);
    const increment = () => count.value++;

    // 暴露 increment 方法给父组件
    expose({
   
   
      increment
    });

    // 返回一个渲染函数
    return () => h('div', count.value);
  }
};

这种灵活的返回机制使得 setup 函数既能满足常规的模板驱动开发,也能应对更复杂的、需要程序化控制渲染的需求。

1.2 响应式 API:refreactive

Vue 3 的响应式系统是其核心特性之一,而 refreactive 是 Composition API 中创建响应式数据的两个主要工具。它们都基于 ES6 的 Proxy 对象实现,能够拦截对数据的访问和修改,从而实现依赖收集和自动更新视图 。尽管它们的目标相似,但在使用方式、适用场景和行为上存在显著差异。

1.2.1 ref:处理基本类型与引用类型

ref 函数用于创建一个响应式的引用(reference)。它可以将任何类型的值(包括基本类型如 string, number, boolean,以及引用类型如 object, array)包装成一个带有 .value 属性的响应式对象 。当访问或修改 .value 时,Vue 的响应式系统会进行追踪和触发更新。

  • 基本类型: 对于基本类型,ref 是实现响应式的唯一选择,因为 reactive 只能处理对象。
  • 引用类型: ref 也可以处理对象和数组。当 ref 的值是一个对象时,Vue 会通过 reactive 自动将其转换为深层响应式代理 。这意味着你可以直接修改嵌套对象的属性,并且这些修改会被检测到。

在 JavaScript 代码中,必须通过 .value 来访问或修改 ref 所包装的值。然而,在模板中,Vue 会自动“解包” ref,因此可以直接使用变量名,无需 .value

import {
   
    ref } from 'vue';

const count = ref(0); // 基本类型
const state = ref({
   
    name: 'Vue' }); // 引用类型

console.log(count.value); // 0
count.value++;
console.log(state.value.name); // 'Vue'
state.value.name = 'Vue 3';

ref 的一个重要优势是,当你重新为 .value 赋一个新对象时,其响应性不会丢失,这与 reactive 的行为不同 。

1.2.2 reactive:处理复杂对象

reactive 函数用于将一个普通对象转换为一个深层响应式的代理对象。与 ref 不同,reactive 直接作用于对象本身,返回的代理对象与原始对象不相等 。对代理对象的任何属性(包括嵌套属性)的修改都会被 Vue 的响应式系统追踪。

reactive 非常适合用于管理复杂的状态对象,例如一个包含多个嵌套属性和数组的表单数据。它默认是深层响应的,这意味着对象内部的所有嵌套对象和数组都会被递归地转换为响应式代理 。

import {
   
    reactive } from 'vue';

const formState = reactive({
   
   
  user: {
   
   
    name: '',
    age: null
  },
  preferences: ['reading', 'coding']
});

// 直接修改属性
formState.user.name = 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

步子哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值