Vue 3源码极客之:`Vue`的`Composition API`:如何实现自定义的`Composable`函数。

各位观众老爷,晚上好!今儿咱们聊聊Vue 3 Composition API 里头,怎么玩转自定义 Composable 函数。这玩意儿,说白了,就是把一堆逻辑攒成一个函数,然后在不同的组件里复用。听起来是不是有点像“祖传秘方”?

一、啥是Composable?为啥要用它?

在Vue 2的Options API里,逻辑往往分散在datamethodscomputed等等选项里,组件稍微复杂点,代码就跟“千层饼”似的,揉成一团,难以维护和复用。

Composable 就好比“模块化装修”,把不同的功能模块(比如处理用户输入、网络请求、动画效果)封装成一个个独立的函数,哪个组件需要,直接“插”进去就行了,清晰又高效。

用人话说,Composable 就是:

  • 代码复用: 一份逻辑,到处使用,告别复制粘贴。
  • 逻辑组织: 功能内聚,代码结构清晰,维护起来心情舒畅。
  • 可测试性: 独立的Composable 函数,更容易进行单元测试。

二、Composable 函数的“套路”

一个 Composable 函数,通常遵循以下几个步骤:

  1. 定义函数: 函数名通常以 use 开头,比如 useMousePositionuseCounter
  2. 引入依赖: 使用 refreactivecomputed 等 Vue 提供的 API 来管理状态和计算属性。
  3. 编写逻辑: 在函数内部实现具体的业务逻辑。
  4. 返回响应式数据: 将需要暴露给组件的状态和方法,以对象的形式返回。

三、实战演练:编写一个 useMousePosition Composable

咱们来编写一个获取鼠标位置的 Composable 函数 useMousePosition

import { ref, onMounted, onUnmounted } from 'vue';

export function useMousePosition() {
  const x = ref(0);
  const y = ref(0);

  function updatePosition(event) {
    x.value = event.clientX;
    y.value = event.clientY;
  }

  onMounted(() => {
    window.addEventListener('mousemove', updatePosition);
  });

  onUnmounted(() => {
    window.removeEventListener('mousemove', updatePosition);
  });

  return {
    x,
    y,
  };
}

代码解读:

  • ref(0):创建两个响应式变量 xy,初始值为 0。
  • updatePosition(event):更新鼠标位置的回调函数。
  • onMounted():在组件挂载后,添加 mousemove 事件监听器。
  • onUnmounted():在组件卸载前,移除 mousemove 事件监听器,防止内存泄漏。
  • return { x, y }:返回包含 xy 的对象,供组件使用。

四、在组件中使用 useMousePosition

现在,咱们在一个组件中使用 useMousePosition

<template>
  <div>
    <p>鼠标位置:X: {{ x }}, Y: {{ y }}</p>
  </div>
</template>

<script>
import { useMousePosition } from './useMousePosition';
import { defineComponent } from 'vue';

export default defineComponent({
  setup() {
    const { x, y } = useMousePosition();

    return {
      x,
      y,
    };
  },
});
</script>

代码解读:

  • import { useMousePosition } from './useMousePosition':引入 useMousePosition 函数。
  • const { x, y } = useMousePosition():调用 useMousePosition 函数,获取 xy 变量。
  • return { x, y }:将 xy 变量暴露给模板。
  • {{ x }}, {{ y }}:在模板中显示鼠标位置。

五、进阶技巧:Composable 函数的参数和返回值

Composable 函数可以接收参数,也可以返回更复杂的数据结构。

1. 接收参数

假设我们需要一个可以自定义更新频率的 useMousePosition 函数:

import { ref, onMounted, onUnmounted } from 'vue';

export function useMousePosition(interval = 100) { // 添加 interval 参数,默认 100ms
  const x = ref(0);
  const y = ref(0);

  let timer = null;

  function updatePosition(event) {
    x.value = event.clientX;
    y.value = event.clientY;
  }

  onMounted(() => {
    timer = setInterval(() => {
      updatePosition({ clientX: document.body.clientWidth * Math.random(), clientY: document.body.clientHeight * Math.random() }); // 模拟鼠标移动
    }, interval);
  });

  onUnmounted(() => {
    clearInterval(timer);
  });

  return {
    x,
    y,
  };
}

在组件中使用:

<template>
  <div>
    <p>鼠标位置:X: {{ x }}, Y: {{ y }}</p>
  </div>
</template>

<script>
import { useMousePosition } from './useMousePosition';
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const interval = ref(500);
    const { x, y } = useMousePosition(interval.value);

    return {
      x,
      y,
      interval,
    };
  },
});
</script>

2. 返回复杂数据结构

Composable 函数可以返回对象、数组,甚至函数:

import { ref, onMounted, onUnmounted } from 'vue';

export function useCounter(initialValue = 0) {
  const count = ref(initialValue);

  function increment() {
    count.value++;
  }

  function decrement() {
    count.value--;
  }

  function reset() {
    count.value = initialValue;
  }

  return {
    count,
    increment,
    decrement,
    reset,
  };
}

在组件中使用:

<template>
  <div>
    <p>计数器:{{ count }}</p>
    <button @click="increment">增加</button>
    <button @click="decrement">减少</button>
    <button @click="reset">重置</button>
  </div>
</template>

<script>
import { useCounter } from './useCounter';
import { defineComponent } from 'vue';

export default defineComponent({
  setup() {
    const { count, increment, decrement, reset } = useCounter(10);

    return {
      count,
      increment,
      decrement,
      reset,
    };
  },
});
</script>

六、最佳实践:Composable 函数的设计原则

  • 单一职责: 一个 Composable 函数只负责一个特定的功能。
  • 可配置性: 允许通过参数自定义 Composable 函数的行为。
  • 可测试性: 编写单元测试,确保 Composable 函数的正确性。
  • 命名规范: 函数名以 use 开头,清晰表达函数的功能。
  • 文档: 编写详细的文档,说明 Composable 函数的功能、参数和返回值。

七、Composable 函数的应用场景

Composable 函数可以应用于各种场景:

  • 表单处理: 验证用户输入、管理表单状态。
  • 数据获取: 从 API 获取数据、缓存数据。
  • 动画效果: 创建复杂的动画效果。
  • 权限控制: 检查用户权限。
  • 国际化: 处理多语言支持。

八、Composable 函数与 Vuex 的区别

Composable 函数和 Vuex 都是用于状态管理的工具,但它们的应用场景有所不同。

特性 Composable 函数 Vuex
状态管理范围 组件级别,适用于组件内部的状态管理,或者在少量组件之间共享状态。 应用级别,适用于需要在多个组件之间共享的全局状态。
代码复用 通过函数调用实现代码复用,更加灵活。 通过 Store 的 mutations 和 actions 实现代码复用,相对固定。
复杂性 相对简单,易于理解和使用。 相对复杂,需要理解 Store、mutations、actions、getters 等概念。
适用场景 组件内部状态管理、少量组件之间共享状态、简单的逻辑复用。 全局状态管理、多个组件之间共享状态、复杂的应用逻辑。
测试 方便单元测试,直接调用函数并验证返回值即可。 需要模拟 Vuex 环境进行测试,相对复杂。

简单来说,如果只是在几个组件之间共享状态,或者需要复用一些简单的逻辑,Composable 函数更合适。如果需要管理全局状态,并且应用逻辑比较复杂,Vuex 更加强大。

九、总结

Composable 函数是 Vue 3 Composition API 的核心特性之一,它提供了一种更加灵活和可维护的代码组织方式。掌握 Composable 函数的编写和使用,可以大大提高开发效率和代码质量。

记住,Composable 的精髓在于:模块化,复用,清晰!

希望今天的内容对大家有所帮助。溜了溜了~

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注