- API 路由统一: 移除 /api/h5/,统一 /api/pms/ 入口 - 二进制改名: api-server -> pms-api(与服务名一致) - nginx: 合并 /api/ + /pms/api/ 为 /api/pms/ - 新增 docs/API.md 接口文档 - 后端: CabinetDetail 增加 rssi/pow_fail/address/soc 等字段 - Redis 管道批量读取仓体实时数据(SOC/电压/电流/功率) - DB: 所有 TIMESTAMP 列改为 DATETIME(修复类型不匹配) - 用户管理: 新增创建时间/修改时间列,状态改用 Switch - 角色管理/用户管理: 操作按钮改为图标 - PC 设备详情: 基本信息补全 RSSI/ICCID/电源/地址 - PC 卡片: 通道统计改用 Lucide 图标,颜色统一 - PC 二维码: canvas 渲染,含编号文字,支持右键下载 - H5: 按 uni-app 规范修正导航(navigateTo 替代 reLaunch) - H5: 详情页加返回导航条 - H5: 扫码支持摄像头(jsQR)+ ?id= 参数 - H5: 底部 tabBar 在所有页面常显 - H5: 首页统计改为 6 项(设备/通道/空闲/充电/充满/故障) - H5: 下拉刷新 / 上拉加载 - 产品首页: 添加业务联系 - 文档: 清理过时文件,补充缺失规范章节
5.2 KiB
5.2 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Use Composables Instead of Mixins for Logic Reuse | HIGH | Mixins cause naming conflicts, unclear data origins, and inflexible logic - composables solve all these problems | best-practice |
|
Use Composables Instead of Mixins for Logic Reuse
Impact: HIGH - Mixins, the primary logic reuse mechanism in Options API, have fundamental flaws that make code hard to maintain. Composables (Composition API functions) solve all mixin drawbacks: unclear property origins, naming conflicts, and inability to parameterize.
The ability to create clean, reusable logic through composables is the primary advantage of the Composition API.
Task Checklist
- Migrate existing mixins to composables when refactoring
- Never create new mixins - use composables instead
- Use explicit imports to make data origins clear
- Parameterize composables to make them flexible
- Prefix composables with "use" (useAuth, useFetch, useForm)
Problems with Mixins:
// userMixin.js
export const userMixin = {
data() {
return {
user: null,
loading: false // Conflict waiting to happen!
}
},
methods: {
fetchUser() { /* ... */ }
}
}
// authMixin.js
export const authMixin = {
data() {
return {
token: null,
loading: false // NAME CONFLICT with userMixin!
}
},
methods: {
login() { /* ... */ }
}
}
// Component using mixins - PROBLEMATIC
export default {
mixins: [userMixin, authMixin],
mounted() {
// PROBLEM 1: Where does 'user' come from? Have to check mixins
console.log(this.user)
// PROBLEM 2: Which 'loading'? Last mixin wins, silently!
console.log(this.loading) // Is this user loading or auth loading?
// PROBLEM 3: Can't customize behavior per-component
this.fetchUser() // Always fetches the same way
}
}
Composables Solution:
// composables/useUser.js
import { ref } from 'vue'
export function useUser(userId) { // Can accept parameters!
const user = ref(null)
const loading = ref(false)
const error = ref(null)
async function fetchUser() {
loading.value = true
try {
user.value = await api.getUser(userId)
} catch (e) {
error.value = e
} finally {
loading.value = false
}
}
return { user, loading, error, fetchUser }
}
// composables/useAuth.js
import { ref } from 'vue'
export function useAuth() {
const token = ref(null)
const loading = ref(false) // No conflict - it's scoped!
async function login(credentials) { /* ... */ }
function logout() { /* ... */ }
return { token, loading, login, logout }
}
// Component using composables - CLEAR AND FLEXIBLE
<script setup>
import { useUser } from '@/composables/useUser'
import { useAuth } from '@/composables/useAuth'
// SOLUTION 1: Clear where everything comes from
const { user, loading: userLoading, fetchUser } = useUser(123)
const { token, loading: authLoading, login } = useAuth()
// SOLUTION 2: Rename to avoid any conflicts
// userLoading vs authLoading - explicit!
// SOLUTION 3: Parameterize behavior
const adminUser = useUser(adminId)
const currentUser = useUser(currentUserId)
// Each has its own state!
onMounted(() => {
fetchUser() // Explicitly from useUser
})
</script>
Migrating from Mixins
// BEFORE: Mixin with options
export const formMixin = {
data() {
return { errors: {}, submitting: false }
},
methods: {
validate() { /* ... */ },
submit() { /* ... */ }
}
}
// AFTER: Composable with flexibility
export function useForm(initialValues, validationSchema) {
const values = ref({ ...initialValues })
const errors = ref({})
const submitting = ref(false)
const touched = ref({})
function validate() {
errors.value = validationSchema.validate(values.value)
return Object.keys(errors.value).length === 0
}
async function submit(onSubmit) {
if (!validate()) return
submitting.value = true
try {
await onSubmit(values.value)
} finally {
submitting.value = false
}
}
function reset() {
values.value = { ...initialValues }
errors.value = {}
touched.value = {}
}
return {
values,
errors,
submitting,
touched,
validate,
submit,
reset
}
}
// Usage - now parameterizable and explicit
const loginForm = useForm(
{ email: '', password: '' },
loginValidationSchema
)
const registerForm = useForm(
{ email: '', password: '', name: '' },
registerValidationSchema
)
Composition Over Mixins Benefits
| Aspect | Mixins | Composables |
|---|---|---|
| Property origin | Unclear | Explicit import |
| Naming conflicts | Silent overwrites | Explicit rename |
| Parameters | Not possible | Fully supported |
| Type inference | Poor | Excellent |
| Reuse instances | One per component | Multiple allowed |
| Tree-shaking | Not possible | Fully supported |