Composables和Utils的区别(自学用)

一句话总结核心区别:

  • 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

js 复制代码
// 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

ts 复制代码
// 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

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

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

TypeScript

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

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

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

1. 定义

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

命名规范:必须以 use开头 (如 useUser、useWindowSize)。

2. 核心特点

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

3. 适用场景

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

4. 代码示例

Javascript

js 复制代码
// 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

ts 复制代码
// 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

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

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

TypeScript

ts 复制代码
<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

plain 复制代码
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、是否需要响应式状态
相关推荐
小码哥_常7 小时前
一套自己部署的 IM,客户端是同一份 Flutter
前端·后端
蜗牛攻城狮9 小时前
微前端四大方案选型对比与最佳实践:qiankun、micro-app、wujie、Module Federation
前端·微前端·qiankun·前端架构·micro-app·wujie
她说可以呀11 小时前
Vue插槽
前端·javascript·vue.js
qq_4480111611 小时前
Vue + Jinja2两套模版与法冲突造成undefinedError
前端·javascript·vue.js
aixingpan11 小时前
aixingpan.cn API开发文档:前端查询夏令时和时区接口指南
前端·php
2601_9583529012 小时前
23.5×19mm 换掉一整条音频链?|A-68 集成了什么:ADC / DAC / 24.576MHz 时钟 / AEC / 波束降噪,主控仅需 6 根线
前端·音视频·ai降噪·回声消除·语音模块
志尊宝12 小时前
AI原生前端开发(039):AI辅助的Nuxt 4全栈开发
前端·vue.js·ai-native
FRing0313 小时前
JavaScript入门到精通三十二(深拷贝、浅拷贝、递归函数)
开发语言·前端·javascript
excel14 小时前
双 Token 与前端使用 Crypto 公私钥鉴权换 Token,哪种方案更好?
前端·后端
外收内放14 小时前
Python基础语法练习题(73)
java·前端·python