Composables和Utils的区别(自学用)

7 阅读4分钟

一句话总结核心区别:

  • Utils(工具函数)纯数据 / 逻辑处理,无状态、无 Vue 特性,通用、可在任何框架 / 无框架使用。
  • Composables(组合式函数)Vue 专属的状态化逻辑封装,依赖 Vue API(ref/reactive/watch 等),带响应式状态、生命周期,只能在 Vue 组件 / 其他 Composables 中使用。

一、Utils(工具函数)详解

1. 定义

utils通用工具函数,本质是纯函数(优先),用来封装与框架无关的通用逻辑,只负责「输入 → 处理 → 输出」,不依赖任何前端框架(Vue/React/ 原生 JS 都能用)。

2. 核心特点

  1. 无 Vue 依赖:不使用 ref/reactive/watch/ 生命周期等任何 Vue API
  2. 无状态 / 无副作用:相同输入永远返回相同输出,不修改外部变量
  3. 通用性极强:可在 Vue 组件、JS 文件、Node.js 中任意调用
  4. 轻量、独立:一个函数只做一件事(格式化、计算、校验等)

3. 适用场景

  • 数据格式化(时间、金额、手机号)
  • 字符串 / 数组 / 对象操作
  • 表单校验、数学计算
  • 本地存储封装(简单版)、通用常量

4. 代码示例

Javascript

// src/utils/format.js
// 纯工具函数:和 Vue 无关,任何项目都能用
export function formatMoney(num) {
  if (isNaN(num)) return '0.00'
  return Number(num).toFixed(2)
}

export function formatDate(timestamp) {
  const date = new Date(timestamp)
  return date.toLocaleDateString()
}

TypeScript

// src/utils/format.ts
// 纯工具函数:和 Vue 无关,任何项目都能用

/**
 * 格式化金额(保留两位小数)
 * @param num - 数字或数字字符串
 * @returns 格式化后的金额字符串
 */
export function formatMoney(num: number | string): string {
  const n = typeof num === 'string' ? parseFloat(num) : num
  if (isNaN(n)) return '0.00'
  return n.toFixed(2)
}

/**
 * 格式化时间戳为本地日期字符串
 * @param timestamp - 毫秒时间戳
 * @returns 格式化后的日期字符串
 */
export function formatDate(timestamp: number): string {
  const date = new Date(timestamp)
  return date.toLocaleDateString()
}

5. 使用方式

Javascript

<script setup>
    // 直接调用,无需关心生命周期、响应式
    import { formatMoney } from '@/utils/format'

    const price = 99
    console.log(formatMoney(price)) // 99.00
</script>

TypeScript

<script setup lang="ts">
    import { formatMoney } from '@/utils/format'

    const price = 99
    console.log(formatMoney(price)) // "99.00"
</script>

二、Composables(组合式函数)详解

1. 定义

composablesVue 组合式函数,专门用来封装带 Vue 响应式 / 生命周期的复用逻辑,是 Vue 3 组合式 API 的核心实践。

命名规范:必须以 use 开头(如 useUseruseWindowSize)。

2. 核心特点

  1. 强依赖 Vue:必须使用 Vue 提供的 API(ref/reactive/watch/onMounted 等)
  2. 自带响应式状态:内部管理响应式数据,可被组件共享、追踪变化
  3. 绑定 Vue 生命周期:能监听组件挂载、卸载,处理副作用
  4. 组件专属:只能在 <script setup> 或其他 Composables 中使用
  5. 逻辑聚合:把「数据 + 方法 + 监听」打包复用

3. 适用场景

  • 响应式数据逻辑(窗口大小、滚动位置、计时器)
  • 业务状态共享(用户信息、购物车、表单状态)
  • 生命周期副作用(定时器、事件监听、接口轮询)
  • 跨组件复用的 Vue 专属逻辑

4. 代码示例

Javascript

// src/composables/useWindowSize.js
// Vue 专属组合式函数:依赖响应式 + 生命周期
import { ref, onMounted, onUnmounted } from 'vue'

export function useWindowSize() {
  // 响应式状态(Vue API)
  const width = ref(window.innerWidth)
  const height = ref(window.innerHeight)

  // 事件处理函数
  function updateSize() {
    width.value = window.innerWidth
    height.value = window.innerHeight
  }

  // 绑定 Vue 生命周期
  onMounted(() => window.addEventListener('resize', updateSize))
  onUnmounted(() => window.removeEventListener('resize', updateSize))

  // 返回响应式状态和方法
  return { width, height }
}

TypeScript

// src/composables/useWindowSize.ts
// Vue 专属组合式函数:依赖响应式 + 生命周期
import { ref, onMounted, onUnmounted, type Ref } from 'vue'

interface WindowSize {
  width: Ref<number>
  height: Ref<number>
}

export function useWindowSize(): WindowSize {
  // 响应式状态(Vue API)
  const width = ref<number>(window.innerWidth)
  const height = ref<number>(window.innerHeight)

  // 事件处理函数
  function updateSize(): void {
    width.value = window.innerWidth
    height.value = window.innerHeight
  }

  // 绑定 Vue 生命周期
  onMounted(() => window.addEventListener('resize', updateSize))
  onUnmounted(() => window.removeEventListener('resize', updateSize))

  // 返回响应式状态
  return { width, height }
}

5. 使用方式

Javascript

<script setup>
// 直接使用响应式数据,自动跟随变化
import { useWindowSize } from '@/composables/useWindowSize'
const { width, height } = useWindowSize()
</script>

<template>
  <div>窗口宽度:{{ width }}</div>
  <div>窗口高度:{{ height }}</div>
</template>

TypeScript

<script setup lang="ts">
import { useWindowSize } from '@/composables/useWindowSize'

const { width, height } = useWindowSize()
</script>

<template>
  <div>窗口宽度:{{ width }}</div>
  <div>窗口高度:{{ height }}</div>
</template>

三、Composables 和 Utils 核心区别对比表

维度Utils(工具函数)Composables(组合式函数)
依赖无框架依赖,纯 JS强依赖 Vue 3 组合式 API
状态无状态、纯函数响应式状态,可管理数据
生命周期无关,不依赖组件生命周期可使用 onMounted
等 Vue 生命周期
复用范围全场景通用(Vue/React/Node/ 原生 JS)仅 Vue 组件 / 其他 Composables 中使用
核心作用数据处理、工具方法封装带响应式的业务逻辑、组件逻辑复用
命名随意(camelCase)强制 useXxx
开头
例子formatMoney
debounce
validatePhone
useUser
useWindowSize
useForm

四、最简单的区分方法

  1. 如果逻辑里用到了 ref/reactive/watch/onMounted → 一定放在 composables
  2. 如果只是纯数据计算、格式化、通用方法 → 一定放在 utils
  3. Composables 可以调用 Utils,但 Utils 绝对不能调用 Composables(工具不能依赖框架)

五、标准项目目录结构

plaintext

src/
├── composables/  # Vue 组合式函数(响应式、生命周期)
│   ├── useUser.js
│   ├── useWindowSize.js
│   └── useCart.js
├── utils/        # 通用工具函数(纯 JS、无状态)
│   ├── format.js
│   ├── validate.js
│   └── common.js

总结

  1. Utils = 通用工具,纯 JS,无状态,哪里都能用
  2. Composables = Vue 逻辑复用,带响应式 + 生命周期,只能在 Vue 中用
  3. 区分关键:是否使用 Vue API、是否需要响应式状态