模块化设计的最佳实践有哪些?
1. 单一职责原则
每个模块只做一件事。
// ❌ 混合职责
// user.js
export function fetchUser() { ... }
export function formatUser() { ... }
export function validateUser() { ... }
export function sendEmail() { ... }
// ✅ 拆分
// userApi.js → fetchUser
// userFormat.js → formatUser
// userValidate.js → validateUser
// emailService.js → sendEmail2. 显式依赖
// ❌ 隐式依赖(全局变量)
function getUser() {
return fetch(API_URL) // API_URL 从哪来?
}
// ✅ 显式导入
import { API_URL } from './config.js'
function getUser() {
return fetch(API_URL)
}3. 最小导出
只导出外部需要的 API,内部实现保持私有。
// ✅ 使用闭包或 # 私有字段
export class Stack {
#items = [] // 私有
push(item) { this.#items.push(item) }
pop() { return this.#items.pop() }
get size() { return this.#items.length }
}4. 统一导出入口
// utils/index.js(barrel file)
export { formatDate } from './date.js'
export { formatCurrency } from './currency.js'
export { debounce, throttle } from './function.js'
// 使用方只需一个 import
import { formatDate, debounce } from './utils'⚠️ Barrel file 可能影响 Tree Shaking,打包工具需要配置好。
5. 避免副作用
// ❌ 模块导入时有副作用
// global.js
window.myConfig = { ... }
document.title = 'My App'
// ✅ 显式调用
// global.js
export function init() {
window.myConfig = { ... }
}
// app.js
import { init } from './global.js'
init() // 显式调用6. 接口稳定
// ✅ 接口设计稳定、向后兼容
export function createLogger(options) {
return {
log: (msg) => { ... },
error: (msg) => { ... },
warn: (msg) => { ... }
}
}
// ❌ 每次升级都改 API
// v1: export function log(msg) {}
// v2: export function log(level, msg) {} // 破坏性变更7. 按功能分目录
src/
├── api/ # API 请求
│ ├── user.js
│ └── order.js
├── components/ # UI 组件
│ ├── Button.vue
│ └── Input.vue
├── composables/ # 组合式函数
│ ├── useUser.js
│ └── useCart.js
├── utils/ # 工具函数
├── stores/ # 状态管理
├── types/ # TypeScript 类型
└── views/ # 页面8. 类型优先
// 定义清晰的接口
export interface User {
id: number
name: string
email: string
role: 'admin' | 'user' | 'guest'
}
export interface UserService {
getUser(id: number): Promise<User>
updateUser(id: number, data: Partial<User>): Promise<User>
}9. 依赖方向单一
// ✅ 单向依赖
views → composables → api → utils
// ❌ 循环依赖
api → utils → api10. 可测试性
// ✅ 依赖注入,方便测试
export function createUserService(apiClient) {
return {
getUser: (id) => apiClient.get(`/users/${id}`)
}
}
// 测试时可以注入 mock
const mockApi = { get: jest.fn().mockResolvedValue({ id: 1, name: 'Tom' }) }
const service = createUserService(mockApi)11. 文件命名规范
组件:PascalCase(Button.vue, UserList.vue)
工具:camelCase(formatDate.js, useUser.js)
常量:UPPER_SNAKE_CASE(API_CONFIG.js)
类型:PascalCase(User.ts, ApiResponse.ts)12. 合理使用默认导出和命名导出
// 组件/类 → 默认导出
export default class User { }
// 工具函数/常量 → 命名导出
export function formatDate() { }
export const API_URL = '/api'
// 混用时保持一致
export function helper() { }
export default function main() { }总结
好的模块化设计 = 高内聚 + 低耦合 + 单一职责 + 显式依赖