- 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: 下拉刷新 / 上拉加载 - 产品首页: 添加业务联系 - 文档: 清理过时文件,补充缺失规范章节
3.6 KiB
3.6 KiB
Correct Nesting Order: RouterView, Transition, KeepAlive, Suspense
Rule
When combining <Suspense> with <Transition>, <KeepAlive>, and <RouterView>, the nesting order must be: RouterView -> Transition -> KeepAlive -> Suspense. Incorrect nesting causes components to not work together properly.
Why This Matters
Each of these components wraps and controls its child in specific ways. Incorrect nesting leads to:
- Transitions not animating
- Components not being cached by KeepAlive
- Suspense not catching async dependencies
- Subtle bugs that are hard to diagnose
Bad Code
<template>
<!-- Wrong order - Suspense wrapping KeepAlive -->
<RouterView v-slot="{ Component }">
<Suspense>
<KeepAlive>
<Transition mode="out-in">
<component :is="Component" />
</Transition>
</KeepAlive>
</Suspense>
</RouterView>
</template>
<template>
<!-- Wrong - KeepAlive outside Transition -->
<RouterView v-slot="{ Component }">
<KeepAlive>
<Transition mode="out-in">
<Suspense>
<component :is="Component" />
</Suspense>
</Transition>
</KeepAlive>
</RouterView>
</template>
Good Code
<template>
<RouterView v-slot="{ Component }">
<template v-if="Component">
<Transition mode="out-in">
<KeepAlive>
<Suspense>
<!-- Main content -->
<component :is="Component" />
<!-- Loading state -->
<template #fallback>
<div class="route-loading">
Loading...
</div>
</template>
</Suspense>
</KeepAlive>
</Transition>
</template>
</RouterView>
</template>
With Selective KeepAlive
<template>
<RouterView v-slot="{ Component, route }">
<template v-if="Component">
<Transition :name="route.meta.transition || 'fade'" mode="out-in">
<KeepAlive :include="cachedViews">
<Suspense :timeout="200">
<component :is="Component" :key="route.fullPath" />
<template #fallback>
<RouteLoadingSkeleton :route="route" />
</template>
</Suspense>
</KeepAlive>
</Transition>
</template>
</RouterView>
</template>
<script setup>
import { computed } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
// Only cache specific routes
const cachedViews = computed(() => ['Dashboard', 'Profile', 'Settings'])
</script>
Why This Order?
- RouterView - Provides the route component via scoped slot
- Transition - Needs to wrap what animates (the cached/suspended content)
- KeepAlive - Caches the Suspense + component together
- Suspense - Directly wraps the async component to handle loading
Important Notes
- Vue Router's lazy-loaded route components (via dynamic imports) don't trigger Suspense directly
- Only async components within the route component trigger Suspense
- Use
v-if="Component"to handle the case when no route matches - The
:keyon the component can force re-render and re-trigger Suspense
Key Points
- Always follow the order:
RouterView->Transition->KeepAlive->Suspense - Each wrapper serves a specific purpose in this hierarchy
- Use
mode="out-in"on Transition to prevent overlap during route changes - Consider using route-based keys for fine-grained control over when Suspense triggers