mcpField
C
composables

composables

github.com/patternsdev开发257201 次安装28 周前更新
代码仓库

关于 composables

面向 Vue 开发者的组合式函数指南,讲解如何借助 Composition API 封装并复用有状态逻辑,适用于将数据获取、事件处理和计时器等职责从组件中分离。

主要功能

  1. 01复用有状态函数
  2. 02Composition API 模式
  3. 03响应式状态与生命周期钩子
  4. 04向组件返回状态和方法
  5. 05类型推断支持

适用场景

  1. 01在 Vue 组件间共享逻辑
  2. 02提取数据获取行为
  3. 03管理事件监听器或计时器
  4. 04整理复杂的组件逻辑

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/patternsdev/skills --skill composables

安装后在 Claude Code 中输入/composables确认已加载。

详细介绍

组合式函数

目录

组合式函数是使用 Vue 的组合式 API 封装和复用有状态逻辑的函数。它们用简洁、可组合且可在组件间共享的函数,取代了选项式 API 中零散的代码组织方式。

何时使用

  • 需要在多个组件间共享有状态逻辑且不想重复编写时,使用组合式函数
  • 将数据获取、事件监听器或计时器等关注点提取为可复用函数时,组合式函数会很有帮助

使用说明

  • 按照惯例,组合式函数以 use 为前缀命名(例如 useCounter、useWidth)
  • 在组合式函数中使用 ref()、reactive() 和生命周期钩子(onMounted、onBeforeUnmount)
  • 从组合式函数中返回响应式状态和方法,以便在组件模板中使用
  • 相比选项式 API,优先使用组合式函数,以获得更好的代码组织、可复用性和 TypeScript 支持

详情

选项式 API

在 Vue 引入组合式 API 之前,开发者依赖选项式 API来组织组件逻辑,其中包括响应式数据、生命周期方法、计算属性等。选项式 API 允许在特定选项中定义这些内容,如下例所示:



<script>
  export default {
    name: "MyComponent",
    props: {
      // props
    },
    data() {
      // data
    },
    computed: {
      // computed properties
    },
    watch: {
      // properties to watch
    },
    methods: {
      // methods
    },
    created() {
      // lifecyle methods like created
    },
    // ...
  };
</script>


这种方式满足了当时的需求,在 Vue v3 中仍然适用,但随着组件变得更大、更复杂,管理和维护起来可能会很困难。在特定选项中定义组件逻辑会让代码更难阅读和理解,尤其是在处理大型组件时。在这种结构中,提取并复用组件间的通用逻辑也可能很困难。

我们来看一个简单示例:App 组件渲染了两个独立的子组件——Count 和 Width。

<template>
  <div class="App">
    <Count :count="count" :increment="increment" :decrement="decrement" />
    <div id="divider" />
    <Width :width="width" />
  </div>
</template>

<script>
  import Count from "./components/Count.vue";
  import Width from "./components/Width.vue";

  export default {
    name: "App",
    data() {
      return {
        count: 0,
        width: 0,
      };
    },
    mounted() {
      this.handleResize();
      window.addEventListener("resize", this.handleResize);
    },
    beforeUnmount() {
      window.removeEventListener("resize", this.handleResize);
    },
    methods: {
      increment() {
        this.count++;
      },
      decrement() {
        this.count--;
      },
      handleResize() {
        this.width = window.innerWidth;
      },
    },
    components: {
      Count,
      Width,
    },
  };
</script>

上面的代码片段是一个名为 App 的 Vue 单文件组件(SFC)。

<template> 部分定义了组件的标记。在本例中,它包含一个类名为 "App" 的 <div> 元素,其中包裹着两个子组件:<Count> 和 <Width>。这些子组件通过 Vue 的属性绑定语法(:count、:increment、:decrement 和 :width)传入了一些属性。

<script> 部分包含组件的 JavaScript 代码。在组件定义中,包括:

  • data 方法:返回一个包含组件初始数据属性的对象,即初始化为 0 的 count 和 width。
  • mounted() 生命周期钩子:用于在组件挂载到 DOM 后执行代码。在本例中,它会调用 handleResize() 方法,并为 resize 事件添加事件监听器。
  • beforeUnmount() 生命周期钩子:用于在组件卸载并销毁之前执行代码。在这里,它会移除 resize 事件的事件监听器。
  • methods 对象:包含组件的方法。它定义了 increment()、decrement() 和 handleResize() 方法,这些方法会根据特定事件或操作来修改 count 和 width 数据属性。

运行应用后,当前计数和窗口内部宽度会实时显示。用户可以通过 <Count> 组件中的按钮递增或递减计数,与组件进行交互。同样,每当窗口大小发生变化时,宽度也会自动更新。

尽管这个组件规模不大,其中的逻辑却已经相互交织。一部分逻辑用于实现计数器功能,另一部分则与宽度相关。随着组件不断扩大,在组件中组织和定位相关逻辑会变得更加困难。

为了解决这些问题,Vue 团队在 Vue v3 中引入了组合式 API。

组合式 API

组合式 API 可以看作是一组表示 Vue 核心功能的独立函数所组成的 API。这些函数主要在单个 setup() 选项中使用,该选项是使用组合式 API 的入口。



<script>
  export default {
    name: "MyComponent",
    setup() {
      // the setup function
    },
  };
</script>


setup() 函数会在组件创建之前执行,此时组件的 props 已可用。

使用组合式 API,我们可以导入独立函数,以便在组件中使用 Vue 的核心功能。下面我们使用组合式 API 语法,重写之前的计数器和宽度示例。

<template>
  <div class="App">
    <Count :count="count" :increment="increment" :decrement="decrement" />
    <div id="divider" />
    <Width :width="width" />
  </div>
</template>

<script>
  import { ref, onMounted, onBeforeUnmount } from "vue";
  import Count from "./components/Count.vue";
  import Width from "./components/Width.vue";

  export default {
    name: "App",
    setup() {
      const count = ref(0);
      const width = ref(0);

      const increment = () => {
        count.value++;
      };

      const decrement = () => {
        count.value--;
      };

      const handleResize = () => {
        width.value = window.innerWidth;
      };

      onMounted(() => {
        handleResize();
        window.addEventListener("resize", handleResize);
      });

      onBeforeUnmount(() => {
        window.removeEventListener("resize", handleResize);
      });

      return {
        count,
        width,
        increment,
        decrement,
      };
    },
    components: {
      Count,
      Width,
    },
  };
</script>

组件的 <template> 保持不变,但在组件的 <script> 部分,我们现在使用组合式 API 的 setup() 函数。

在 setup() 函数中,我们:

  • 使用 ref() 函数定义响应式变量 count 和 width。该函数接受一个原始值(例如字符串、数字等),并返回一个响应式/可变对象。
  • 还定义了自定义函数 increment()、decrement() 和 handleResize()。这些函数与我们在之前的选项式 API 示例中定义的方法类似。
  • 使用 onMounted() 生命周期函数调用自定义函数 handleResize(),并在组件挂载时为 resize 事件添加事件监听器。同样,我们使用 onBeforeUnmount() 生命周期函数,在组件卸载前移除 resize 事件的事件监听器。
  • 然后返回在 setup() 函数中定义的响应式变量和函数,使它们可以在组件模板中访问。
组合式函数

通过前面的代码示例,你可能仍会疑惑:setup() 函数有什么开发优势?因为看起来它只是要求我们在一个函数中声明组件选项。

采用组合式 API 的一大优势是,能够提取并复用组件之间共享的逻辑。这是因为我们可以直接声明自己的函数,在其中使用 Vue 全局可用的组合式函数,并让这些函数_在多个组件中轻松复用,以实现相同的效果_。

让我们进一步完善前面的计数器和宽度示例,创建可在多个组件中复用的组合式函数来封装共享逻辑。

首先,创建一个名为 useCounter 的组合式函数,用于封装计数器功能,并返回 count 的当前值、increment() 方法和 decrement() 方法。

按照惯例,组合式函数的名称以“use”开头。

import { ref } from "vue";

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

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

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

  return {
    count,
    increment,
    decrement,
  };
}

同样,我们可以创建一个名为 useWidth() 的组合式函数,用于封装应用的宽度功能。

import { ref, onMounted, onBeforeUnmount } from "vue";

export function useWidth() {
  const width = ref(0);

  function handleResize() {
    width.value = window.innerWidth;
  }

  onMounted(() => {
    handleResize();
    window.addEventListener("resize", handleResize);
  });

  onBeforeUnmount(() => {
    window.removeEventListener("resize", handleResize);
  });

  return {
    width,
  };
}

现在,我们可以在 App 组件中使用这些组合式函数来实现相同的效果:

<template>
  <div class="App">
    <Count :count="count" :increment="increment" :decrement="decrement" />
    <div id="divider" />
    <Width :width="width" />
  </div>
</template>

<script>
  import Count from "./components/Count.vue";
  import Width from "./components/Width.vue";
  import { useCounter } from "./composables/useCounter";
  import { useWidth } from "./composables/useWidth";

  export default {
    name: "App",
    components: {
      Count,
      Width,
    },
    setup() {
      const { count, increment, decrement } = useCounter(0);
      const { width } = useWidth();

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

经过这些修改后,应用的功能与之前相同,但结构更具组合性。

在组合式 API 中使用组合式函数,我们能够将应用的上下文拆分成更小、可复用的部分,从而分离逻辑。

在 Vue 中使用组合式函数,让我们更容易将组件逻辑拆分成多个更小的部分。现在,复用相同的有状态逻辑也变得轻而易举,因为我们不再受限于在选项式 API 的特定选项中组织代码。

组合式函数让我们可以灵活地提取并复用组件之间共享的逻辑。职责分离让我们能够专注于每个组合式函数中的特定功能,使代码更加模块化且易于维护。

将逻辑拆分成更小、可复用的部分后,我们可以通过组合这些组合式函数来构建组件,汇集所需功能,同时避免代码重复。这种方法有助于提高代码复用性,并降低代码重复和不一致的风险。

此外,使用组合式 API 可以提高组件逻辑的可读性和易理解性。每个组合式函数都会封装组件行为的一个特定方面,因此更容易分析和测试。它也便于团队成员协作,因为代码结构更加清晰、有条理。

最后,使用组合式 API 构建 Vue 应用还能带来更好的类型推断。由于组合式 API 帮助我们通过变量和标准 JavaScript 函数来处理组件逻辑,因此使用 TypeScript 这样的静态类型系统构建大型 Vue 应用会容易得多!

来源

参考资料

评论

支持 Markdown