vue-composables

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Vue 3 Composables Style Guide

Vue 3 组合式函数风格指南

Naming Conventions

命名规范

Files

文件命名

  • Prefix with
    use
    and use PascalCase:
    useCounter.ts
    ,
    useApiRequest.ts
  • Place in
    src/composables/
    directory
  • use
    开头并使用大驼峰命名:
    useCounter.ts
    useApiRequest.ts
  • 放置在
    src/composables/
    目录下

Functions

函数命名

  • Use descriptive names:
    useUserData()
    not
    useData()
  • Export as named function:
    export function useCounter() {}
  • 使用描述性名称:
    useUserData()
    而非
    useData()
  • 以命名函数形式导出:
    export function useCounter() {}

Structure Template

结构模板

Follow this order consistently:
ts
import { computed, onMounted, ref, watch } from 'vue'

export function useExample() {
  // 1. Initializing - setup logic, router, external dependencies

  // 2. Primary State - main reactive state
  const data = ref<Data | null>(null)

  // 3. State Metadata - status, errors, loading
  const status = ref<'idle' | 'loading' | 'success' | 'error'>('idle')
  const error = ref<Error | null>(null)

  // 4. Computed - derived state
  const isLoading = computed(() => status.value === 'loading')

  // 5. Methods - state manipulation
  const fetchData = async () => {
    status.value = 'loading'
    try {
      // fetch logic
      status.value = 'success'
    }
    catch (e) {
      status.value = 'error'
      error.value = e instanceof Error ? e : new Error(String(e))
    }
  }

  // 6. Lifecycle Hooks
  onMounted(() => {
    // initialization logic
  })

  // 7. Watchers
  watch(data, (newValue) => {
    // react to changes
  })

  return { data, status, error, isLoading, fetchData }
}
请始终遵循以下顺序:
ts
import { computed, onMounted, ref, watch } from 'vue'

export function useExample() {
  // 1. 初始化 - 设置逻辑、路由、外部依赖

  // 2. 核心状态 - 主要响应式状态
  const data = ref<Data | null>(null)

  // 3. 状态元数据 - 状态、错误、加载状态
  const status = ref<'idle' | 'loading' | 'success' | 'error'>('idle')
  const error = ref<Error | null>(null)

  // 4. 计算属性 - 派生状态
  const isLoading = computed(() => status.value === 'loading')

  // 5. 方法 - 状态操作
  const fetchData = async () => {
    status.value = 'loading'
    try {
      // 请求逻辑
      status.value = 'success'
    }
    catch (e) {
      status.value = 'error'
      error.value = e instanceof Error ? e : new Error(String(e))
    }
  }

  // 6. 生命周期钩子
  onMounted(() => {
    // 初始化逻辑
  })

  // 7. 监听器
  watch(data, (newValue) => {
    // 响应变化
  })

  return { data, status, error, isLoading, fetchData }
}

Core Rules

核心规则

Single Responsibility

单一职责

One composable = one purpose. Avoid mixing unrelated concerns.
ts
// GOOD - focused on one task
export function useCounter() {
  const count = ref(0)
  const increment = () => {
    count.value++
  }
  const decrement = () => {
    count.value--
  }
  return { count, increment, decrement }
}

// BAD - mixing user data and counter
export function useUserAndCounter() {
  const user = ref(null)
  const count = ref(0)
  // ... mixed concerns
}
一个组合式函数对应一个用途。避免混合无关逻辑。
ts
// 良好示例 - 专注单一任务
export function useCounter() {
  const count = ref(0)
  const increment = () => {
    count.value++
  }
  const decrement = () => {
    count.value--
  }
  return { count, increment, decrement }
}

// 不良示例 - 混合用户数据与计数器逻辑
export function useUserAndCounter() {
  const user = ref(null)
  const count = ref(0)
  // ... 混合逻辑
}

Expose Error State

暴露错误状态

Return errors for component handling. Never swallow errors or show UI directly.
ts
// GOOD
const error = ref<Error | null>(null)
try {
  await fetchData()
}
catch (e) {
  error.value = e instanceof Error ? e : new Error(String(e))
}
return { error }

// BAD - swallowing errors or showing UI
try {
  await fetchData()
}
catch (e) {
  console.error(e) // swallowed
  showToast('Error!') // UI in composable
}
返回错误状态供组件处理。绝不能吞掉错误或直接操作UI。
ts
// 良好示例
const error = ref<Error | null>(null)
try {
  await fetchData()
}
catch (e) {
  error.value = e instanceof Error ? e : new Error(String(e))
}
return { error }

// 不良示例 - 吞掉错误或直接操作UI
try {
  await fetchData()
}
catch (e) {
  console.error(e) // 错误被吞掉
  showToast('Error!') // 在组合式函数中操作UI
}

No UI Logic in Composables

组合式函数中不包含UI逻辑

Keep composables focused on state/logic. Handle UI in components.
ts
// GOOD - composable returns state
export function useUserData(userId: string) {
  const user = ref<User | null>(null)
  const error = ref<Error | null>(null)
  const fetchUser = async () => { /* ... */ }
  return { user, error, fetchUser }
}

// Component handles UI
const { error } = useUserData(userId)
watch(error, (e) => {
  if (e)
    showToast('Error occurred')
})
组合式函数应专注于状态与逻辑。UI处理放在组件中。
ts
// 良好示例 - 组合式函数返回状态
export function useUserData(userId: string) {
  const user = ref<User | null>(null)
  const error = ref<Error | null>(null)
  const fetchUser = async () => { /* ... */ }
  return { user, error, fetchUser }
}

// 组件处理UI
const { error } = useUserData(userId)
watch(error, (e) => {
  if (e)
    showToast('发生错误')
})

Object Arguments for 4+ Parameters

4个及以上参数使用对象形式

ts
// GOOD - object for many params
useUserData({ id: 1, fetchOnMount: true, token: 'abc', locale: 'en' })

// GOOD - positional for few params
useCounter(initialValue, step)

// BAD - too many positional args
useUserData(1, true, 'abc', 'en', false, 'default')
ts
// 良好示例 - 多参数使用对象
useUserData({ id: 1, fetchOnMount: true, token: 'abc', locale: 'en' })

// 良好示例 - 少参数使用位置参数
useCounter(initialValue, step)

// 不良示例 - 过多位置参数
useUserData(1, true, 'abc', 'en', false, 'default')

Group Related State into Objects

将相关状态分组为对象

When a composable has 4+ related state properties, group them into a single
ref
object instead of separate refs.
ts
// GOOD - grouped state for 4+ related properties
interface FormState {
  name: string
  email: string
  phone: string
  address: string
}

export function useContactForm() {
  const form = ref<FormState>({
    name: '',
    email: '',
    phone: '',
    address: '',
  })

  function reset() {
    form.value = { name: '', email: '', phone: '', address: '' }
  }

  return { form, reset }
}

// GOOD - separate refs for 1-3 unrelated properties
export function useToggle() {
  const isOpen = ref(false)
  const isLoading = ref(false)
  return { isOpen, isLoading }
}

// BAD - many separate refs for related state
export function useContactForm() {
  const name = ref('')
  const email = ref('')
  const phone = ref('')
  const address = ref('')
  // ... becomes unwieldy to manage and reset
}
当组合式函数包含4个及以上相关状态属性时,将它们分组为单个
ref
对象,而非多个独立的ref。
ts
// 良好示例 - 4个及以上相关属性使用分组状态
interface FormState {
  name: string
  email: string
  phone: string
  address: string
}

export function useContactForm() {
  const form = ref<FormState>({
    name: '',
    email: '',
    phone: '',
    address: '',
  })

  function reset() {
    form.value = { name: '', email: '', phone: '', address: '' }
  }

  return { form, reset }
}

// 良好示例 - 1-3个无关属性使用独立ref
export function useToggle() {
  const isOpen = ref(false)
  const isLoading = ref(false)
  return { isOpen, isLoading }
}

// 不良示例 - 多个独立ref管理相关状态
export function useContactForm() {
  const name = ref('')
  const email = ref('')
  const phone = ref('')
  const address = ref('')
  // ... 管理和重置变得繁琐
}

Functional Core, Imperative Shell

纯函数核心,命令式外壳

Extract pure logic from Vue reactivity when beneficial.
ts
// Pure function (testable, no side effects)
function calculateTotal(items: ReadonlyArray<Item>) {
  return items.reduce((sum, item) => sum + item.price, 0)
}

// Composable uses the pure function
export function useCart() {
  const items = ref<Array<Item>>([])
  const total = computed(() => calculateTotal(items.value))
  return { items, total }
}
在有益的情况下,将纯逻辑从Vue响应式系统中提取出来。
ts
// 纯函数(可测试、无副作用)
function calculateTotal(items: ReadonlyArray<Item>) {
  return items.reduce((sum, item) => sum + item.price, 0)
}

// 组合式函数使用纯函数
export function useCart() {
  const items = ref<Array<Item>>([])
  const total = computed(() => calculateTotal(items.value))
  return { items, total }
}

Quick Reference

快速参考

AspectDoDon't
Naming
useUserData
,
useFetchApi
useData
,
getData
File
useCounter.ts
in
composables/
counter.ts
anywhere
ErrorsReturn
error
ref
console.error()
or toast
UIReturn state, handle UI in component
showModal()
in composable
ParamsObject for 4+ paramsLong positional arg lists
State
ref
object for 4+ related properties
Many separate refs
FocusSingle responsibilityMixed concerns
方面正确做法错误做法
命名
useUserData
useFetchApi
useData
getData
文件
useCounter.ts
放在
composables/
目录
counter.ts
随意放置
错误处理返回
error
ref
使用
console.error()
或弹窗
UI逻辑返回状态,在组件中处理UI在组合式函数中调用
showModal()
参数4个及以上参数使用对象过长的位置参数列表
状态管理4个及以上相关属性使用
ref
对象
多个独立ref管理相关状态
功能聚焦单一职责混合无关逻辑