vue-composables
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVue 3 Composables Style Guide
Vue 3 组合式函数风格指南
Naming Conventions
命名规范
Files
文件命名
- Prefix with and use PascalCase:
use,useCounter.tsuseApiRequest.ts - Place in directory
src/composables/
- 以开头并使用大驼峰命名:
use、useCounter.tsuseApiRequest.ts - 放置在目录下
src/composables/
Functions
函数命名
- Use descriptive names: not
useUserData()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 object instead of separate refs.
refts
// 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。
refts
// 良好示例 - 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
快速参考
| Aspect | Do | Don't |
|---|---|---|
| Naming | | |
| File | | |
| Errors | Return | |
| UI | Return state, handle UI in component | |
| Params | Object for 4+ params | Long positional arg lists |
| State | | Many separate refs |
| Focus | Single responsibility | Mixed concerns |
| 方面 | 正确做法 | 错误做法 |
|---|---|---|
| 命名 | | |
| 文件 | | |
| 错误处理 | 返回 | 使用 |
| UI逻辑 | 返回状态,在组件中处理UI | 在组合式函数中调用 |
| 参数 | 4个及以上参数使用对象 | 过长的位置参数列表 |
| 状态管理 | 4个及以上相关属性使用 | 多个独立ref管理相关状态 |
| 功能聚焦 | 单一职责 | 混合无关逻辑 |