- 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.5 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Use KeepAlive to Preserve Dynamic Component State | MEDIUM | Dynamic component switching destroys and recreates components, losing all internal state unless wrapped in KeepAlive | best-practice |
|
Use KeepAlive to Preserve Dynamic Component State
Impact: MEDIUM - When switching between components using <component :is="...">, Vue destroys the old component and creates a new one. All internal state (form inputs, scroll position, fetched data) is lost. Wrapping dynamic components in <KeepAlive> caches them and preserves their state.
Task Checklist
- Wrap
<component :is>with<KeepAlive>when state preservation is needed - Use
includeandexcludeto control which components are cached - Use
maxto limit cache size and prevent memory issues - Implement
onActivated/onDeactivatedhooks for cache-aware logic - Consider NOT using KeepAlive when fresh state is desired
The Problem: State Loss
<script setup>
import { ref, shallowRef } from 'vue'
import TabA from './TabA.vue'
import TabB from './TabB.vue'
const currentTab = shallowRef(TabA)
</script>
<template>
<button @click="currentTab = TabA">Tab A</button>
<button @click="currentTab = TabB">Tab B</button>
<!-- State is lost when switching tabs! -->
<component :is="currentTab" />
</template>
If TabA has a form with user input, switching to TabB and back resets all input.
Solution: KeepAlive
<template>
<button @click="currentTab = TabA">Tab A</button>
<button @click="currentTab = TabB">Tab B</button>
<!-- State is preserved when switching -->
<KeepAlive>
<component :is="currentTab" />
</KeepAlive>
</template>
Now TabA's state persists even when TabB is displayed.
Controlling What Gets Cached
Include/Exclude by Name
Only cache specific components:
<template>
<!-- Only cache components named 'TabA' or 'TabB' -->
<KeepAlive include="TabA,TabB">
<component :is="currentTab" />
</KeepAlive>
<!-- Cache all except 'HeavyComponent' -->
<KeepAlive exclude="HeavyComponent">
<component :is="currentTab" />
</KeepAlive>
<!-- Using regex -->
<KeepAlive :include="/^Tab/">
<component :is="currentTab" />
</KeepAlive>
<!-- Using array -->
<KeepAlive :include="['TabA', 'TabB', 'Settings']">
<component :is="currentTab" />
</KeepAlive>
</template>
Important: Components must have a name option to be matched:
<!-- TabA.vue -->
<script>
export default {
name: 'TabA' // Required for include/exclude matching
}
</script>
<script setup>
// ...composition API code
</script>
Or in Vue 3.3+ with <script setup>:
<script setup>
defineOptions({
name: 'TabA'
})
</script>
Limit Cache Size
Prevent memory issues with many cached components:
<template>
<!-- Only keep last 5 components in cache -->
<KeepAlive :max="5">
<component :is="currentTab" />
</KeepAlive>
</template>
When cache exceeds max, the least recently accessed component is destroyed.
Lifecycle Hooks: onActivated and onDeactivated
Cached components need special lifecycle hooks:
<!-- TabA.vue -->
<script setup>
import { onMounted, onUnmounted, onActivated, onDeactivated } from 'vue'
// Only called on first mount, NOT when switching back
onMounted(() => {
console.log('TabA mounted (once)')
})
// Only called when truly destroyed, NOT when switching away
onUnmounted(() => {
console.log('TabA unmounted')
})
// Called EVERY time component becomes visible
onActivated(() => {
console.log('TabA activated')
// Refresh data, resume timers, etc.
fetchLatestData()
})
// Called EVERY time component is hidden (but kept in cache)
onDeactivated(() => {
console.log('TabA deactivated')
// Pause timers, save draft, etc.
pauseAutoRefresh()
})
</script>
Common use cases for activation hooks:
- Refresh stale data when returning to a tab
- Resume/pause video or audio playback
- Reconnect/disconnect WebSocket connections
- Save/restore scroll position
- Track analytics for tab views
KeepAlive with Vue Router
For route-based caching:
<!-- App.vue -->
<template>
<router-view v-slot="{ Component }">
<KeepAlive include="Dashboard,Settings">
<component :is="Component" />
</KeepAlive>
</router-view>
</template>
With transition:
<template>
<router-view v-slot="{ Component }">
<Transition name="fade" mode="out-in">
<KeepAlive>
<component :is="Component" :key="$route.fullPath" />
</KeepAlive>
</Transition>
</router-view>
</template>
When NOT to Use KeepAlive
Don't cache when:
<!-- Fresh data needed each time -->
<template>
<!-- NO KeepAlive - want fresh search results each visit -->
<component :is="currentView" />
</template>
- Form should reset between visits
- Data must be fresh (real-time dashboards)
- Component has significant memory footprint
- Security-sensitive data should be cleared
Performance Considerations
<script setup>
import { shallowRef } from 'vue'
import TabA from './TabA.vue'
import TabB from './TabB.vue'
// Use shallowRef for component references
// Regular ref would deeply track the component object unnecessarily
const currentTab = shallowRef(TabA)
</script>