Vue3 Composition API

95 阅读4分钟

Vue 3 的 Composition API 是一套全新的代码组织方式,旨在解决 Options API 在处理复杂组件逻辑时可能出现的碎片化逻辑复用困难的问题。它不是取代 Options API,而是提供了一种更灵活、更强大的替代方案,尤其适合大型项目或逻辑复杂的组件。

核心思想:

  1. 基于函数组织逻辑: 将组件的逻辑(数据、计算属性、方法、生命周期钩子、watch 等)封装在可复用的函数中(称为 Composable Functions 或简称 Composables)。
  2. 逻辑关注点聚合:相关的代码(例如一个功能模块的所有逻辑)集中在一起,而不是分散在 data, methods, computed, watch, lifecycle hooks 等不同的选项中。
  3. 更好的 TypeScript 集成: 对 TypeScript 的类型推断更友好。

核心 API 详解:

  1. setup() 函数:

    • 入口点: Composition API 的核心入口。它在组件实例创建之前执行(在 beforeCreatecreated 生命周期钩子之前),且 thissetup不可用(因为组件实例尚未完全创建)。
    • 参数:
      • props: 组件接收的 props 对象,是响应式的。不要解构 props,否则会失去响应性!如需解构,使用 toRefs(props)toRef(props, 'propName')
      • context: 一个普通对象,包含三个属性:
        • attrs: 非 prop 的 attribute(同 this.$attrs)。
        • slots: 插槽内容(同 this.$slots)。
        • emit: 触发事件的方法(同 this.$emit)。
    • 返回值: 必须返回一个对象或一个渲染函数。返回的对象中的属性方法会暴露给组件的模板(template)和选项中的 this
    import { toRefs } from 'vue';
    
    export default {
      props: {
        title: String
      },
      setup(props, { emit }) {
        // 正确访问响应式 props
        const { title } = toRefs(props); // title 现在是 ref
        console.log(title.value);
    
        // 定义响应式数据、方法等...
        const count = ref(0);
    
        function increment() {
          count.value++;
          emit('count-changed', count.value);
        }
    
        // 暴露给模板和 this
        return {
          title, // 注意:这里暴露的是 ref,模板会自动解包 .value
          count,
          increment
        };
      }
    }
    
  2. 响应式基础 (ref, reactive):

    • ref(value)
      • 创建一个响应式引用对象,包含一个 .value 属性指向内部值。
      • 适用于基本类型 (string, number, boolean, null, undefined, symbol) 或任何类型(对象、数组也可以,但通常对象用 reactive)。
      • 在模板中使用会自动解包(无需写 .value),在 JS 中访问或修改必须用 .value
      • 创建: const count = ref(0);
      • 访问: console.log(count.value); // 0
      • 修改: count.value = 1;
    • reactive(object)
      • 创建一个响应式代理对象(Proxy)。
      • 适用于对象数组
      • 访问和修改属性直接使用对象属性语法,无需 .value
      • 创建: const state = reactive({ count: 0, name: 'Vue' });
      • 访问/修改: console.log(state.count); state.count++; state.name = 'Vue 3';
    • 关键区别:
      • ref 用于基本类型或需要保持引用的对象,通过 .value 访问。
      • reactive 用于对象/数组,直接访问属性。
      • 使用 reactive 的对象,如果整体替换失去响应性state = newObject 不行)。ref.value 可以被替换(count.value = newValue 是响应式的)。
      • 使用 toRefs(reactiveObject) 可以将 reactive 对象的每个属性转换为独立的 ref,便于在返回对象或解构时保持响应性。
  3. 计算属性 (computed):

    • 接收一个 getter 函数,返回一个只读的响应式 ref 对象。
    • 也可以接收一个包含 getset 函数的对象,创建可写的 ref。
    • 只读:
      const doubledCount = computed(() => count.value * 2);
      console.log(doubledCount.value); // 访问值
      
    • 可写:
      const writableComputed = computed({
        get: () => count.value + 1,
        set: (val) => { count.value = val - 1; }
      });
      writableComputed.value = 10; // 触发 set, count.value 变为 9
      
  4. 侦听器 (watch, watchEffect):

    • watch(source, callback, options?)
      • 显式指定要侦听的一个或多个响应式数据源。
      • source:可以是 getter 函数、refreactive 对象、或包含前几种类型的数组。
      • callback(newValue, oldValue, onCleanup):数据变化时执行的回调。onCleanup 用于注册清理副作用函数。
      • options{ deep: true, immediate: true, flush: 'post' } 等。
      watch(count, (newVal, oldVal) => {
        console.log(`Count changed from ${oldVal} to ${newVal}`);
      });
      watch([count, title], ([newCount, newTitle], [oldCount, oldTitle]) => {
        // 侦听多个源
      });
      watch(() => state.some.nested.prop, (newVal) => { ... }, { deep: true }); // 深度侦听对象属性
      
    • watchEffect(effect)
      • 立即执行传入的函数(effect),并在其执行过程中自动追踪函数内使用到的所有响应式依赖。当这些依赖任意一个发生变化时,重新运行该函数。
      • 简洁,适用于依赖关系简单或需要立即执行的场景。
      • 返回一个停止侦听的函数。
      • onInvalidate 参数注册清理函数(等同于 watchonCleanup)。
      const stop = watchEffect((onInvalidate) => {
        console.log(`Count is: ${count.value}, Title is: ${title.value}`);
        // 清理副作用 (例如取消请求、清除定时器)
        onInvalidate(() => {
          clearTimeout(timerId);
        });
      });
      // 稍后停止侦听
      stop();
      
  5. 生命周期钩子:

    • 提供对应的 onX 函数在 setup() 中注册生命周期回调:
      • onBeforeMount / onMounted
      • onBeforeUpdate / onUpdated
      • onBeforeUnmount / onUnmounted (替代 Vue 2 的 beforeDestroy / destroyed)
      • onErrorCaptured
      • onRenderTracked / onRenderTriggered (开发调试)
      • onActivated / onDeactivated (配合 keep-alive)
      • onServerPrefetch (SSR)
    • 用法: 直接导入并在 setup() 中调用。
      import { onMounted, onUnmounted } from 'vue';
      
      setup() {
        onMounted(() => {
          console.log('Component is mounted!');
          // 初始化操作,如添加事件监听器、发起请求
        });
        onUnmounted(() => {
          console.log('Component is unmounted!');
          // 清理操作,如移除事件监听器、取消请求
        });
      }
      
  6. 依赖注入 (provide, inject):

    • 功能同 Options API 的 provide/inject,但以函数形式提供。
    • 祖先组件 provide
      import { provide, ref } from 'vue';
      setup() {
        const location = ref('North Pole');
        provide('location', location); // 提供响应式数据
        provide('updateLocation', (newLoc) => { location.value = newLoc; }); // 提供方法
      }
      
    • 后代组件 inject
      import { inject } from 'vue';
      setup() {
        const location = inject('location'); // 注入值
        const updateLocation = inject('updateLocation'); // 注入方法
        // 可提供默认值
        const nonExistent = inject('key', 'default value');
        return { location, updateLocation };
      }
      

Composition API 的核心优势:

  1. 逻辑复用与封装: 通过 Composables,可以将任意复杂的、与组件无关的逻辑(如数据获取、鼠标跟踪、表单验证、状态管理等)轻松提取、复用和组合。这是 Mixins 难以企及的(命名冲突、来源不清晰)。
  2. 代码组织更灵活: 基于逻辑功能而非选项类型来组织代码。所有与一个特定功能相关的代码(数据、计算、方法、watch、生命周期)都聚集在 setup() 的同一区域,代码可读性和可维护性显著提升,尤其是在大型复杂组件中。
  3. 更小的生产包 & Tree-Shaking: Composition API 的函数是按需导入的。未使用的 API(如 watchEffect)可以被现代打包工具(如 Webpack、Vite、Rollup)Tree-Shake(摇树优化)掉,减小最终打包体积。Options API 的所有选项都是声明式的,难以被 Tree-Shake。
  4. 更好的 TypeScript 支持: 函数式 API 对类型推导非常友好。变量、函数参数和返回值的类型可以清晰地定义和推断,大大提升了使用 TypeScript 开发 Vue 应用的体验。
  5. 更少的 this 上下文问题:setup() 中完全避免了 this,代码更纯粹,逻辑更易于理解和测试(测试时无需模拟组件实例)。

何时选择 Composition API?

  • 强烈推荐: 构建大型应用、复杂组件、需要高度复用逻辑的场景、使用 TypeScript。
  • 仍然可用: 小型应用、简单组件、个人偏好 Options API 风格。

总结: Vue 3 的 Composition API 通过提供基于函数的、灵活的逻辑组织方式,彻底解决了 Vue 2 Options API 在复杂组件开发和逻辑复用上的痛点。它赋能开发者编写出更清晰、更模块化、更易复用、类型更安全的代码,是构建现代 Vue 应用的强大工具集。理解 setup(), ref, reactive, computed, watch/watchEffect, 生命周期钩子以及如何构建 Composables 是掌握 Composition API 的关键。