- 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: 下拉刷新 / 上拉加载 - 产品首页: 添加业务联系 - 文档: 清理过时文件,补充缺失规范章节
4.2 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||
|---|---|---|---|---|---|---|---|---|
| useAttrs() Object Is Not Reactive | MEDIUM | Watching attrs directly does not trigger - use onUpdated() or convert to props | gotcha |
|
useAttrs() Object Is Not Reactive
Impact: MEDIUM - The object returned by useAttrs() is NOT reactive. While it always reflects the latest fallthrough attributes, you cannot use watch() or watchEffect() to observe its changes. Watchers on attrs properties will NOT trigger when attributes change.
Task Checklist
- Never use
watch()to observe attrs changes - it won't trigger - Use
onUpdated()lifecycle hook for side effects based on attrs - Convert frequently-accessed attrs to props if you need reactivity
- Remember attrs ARE always current in templates and event handlers
Incorrect:
<script setup>
import { watch, useAttrs } from 'vue'
const attrs = useAttrs()
// WRONG: This watcher will NEVER trigger when attrs change!
watch(
() => attrs.someAttr,
(newValue) => {
console.log('Attribute changed:', newValue)
// This callback never runs on attr changes
}
)
// WRONG: watchEffect also doesn't track attrs
watchEffect(() => {
console.log(attrs.class) // Only runs on mount, NOT when class changes
})
</script>
Correct:
<script setup>
import { onUpdated, useAttrs } from 'vue'
const attrs = useAttrs()
// CORRECT: Use onUpdated to access latest attrs
onUpdated(() => {
console.log('Current attrs:', attrs)
// Perform side effects with latest attrs here
})
</script>
<script setup>
import { watch } from 'vue'
// CORRECT: If you need reactivity, declare it as a prop
const props = defineProps({
someAttr: String
})
// Now you can watch it
watch(
() => props.someAttr,
(newValue) => {
console.log('Attribute changed:', newValue)
}
)
</script>
Why Attrs Are Not Reactive
Vue's official documentation states:
"Note that although the attrs object here always reflects the latest fallthrough attributes, it isn't reactive (for performance reasons). You cannot use watchers to observe its changes."
This is a deliberate design decision for performance - making attrs reactive would add overhead to every component that uses fallthrough attributes.
When Attrs DO Reflect Current Values
Despite not being reactive, attrs always have current values in these contexts:
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
// In event handlers - attrs are current when handler executes
function handleClick() {
console.log(attrs.class) // Current value at click time
}
</script>
<template>
<!-- In templates - always current -->
<div :class="attrs.class">
{{ attrs.title }}
</div>
<!-- Using v-bind to spread all attrs -->
<input v-bind="attrs" />
</template>
Computed Properties with Attrs
Computed properties that reference attrs will update when the component re-renders:
<script setup>
import { computed, useAttrs } from 'vue'
const attrs = useAttrs()
// This DOES update - because computed re-evaluates on render
const hasCustomClass = computed(() => {
return !!attrs.class
})
</script>
<template>
<div v-bind="attrs">
<span v-if="hasCustomClass">Has custom styling</span>
</div>
</template>
Note: The computed updates because the component re-renders when props/attrs change, not because attrs is reactive.
Alternative: Use getCurrentInstance() (Advanced)
For advanced use cases, you can access attrs through the component instance:
<script setup>
import { watch, getCurrentInstance } from 'vue'
const instance = getCurrentInstance()
// This may work in some cases, but is NOT officially supported
// getCurrentInstance() is an internal API and may change
watch(
() => instance?.attrs.someAttr,
(newValue) => {
console.log('Attribute changed:', newValue)
}
)
</script>
Warning:
getCurrentInstance()is an internal API. PreferonUpdated()or converting to props.