- 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.0 KiB
4.0 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Prefer Templates Over Render Functions for Compiler Optimizations | MEDIUM | Render functions bypass Vue's compile-time optimizations, resulting in more work during updates | best-practice |
|
Prefer Templates Over Render Functions for Compiler Optimizations
Impact: MEDIUM - Vue's template compiler applies automatic optimizations (static hoisting, patch flags, tree flattening) that are impossible with hand-written render functions. Using render functions means every vnode must be traversed and diffed on every update, even if nothing changed.
Only use render functions when you genuinely need full JavaScript power for highly dynamic component logic.
Task Checklist
- Default to templates for all components
- Only use render functions for highly dynamic rendering logic (e.g., dynamic tag selection)
- If using render functions, understand you're opting out of compile-time optimizations
- Consider JSX if you need render function power with better readability
Why Templates Are Faster
Vue's compiler analyzes templates and generates optimized render functions with:
- Static Hoisting: Static elements are created once and reused across re-renders
- Patch Flags: Dynamic bindings are marked so Vue only checks what can change
- Tree Flattening: Only dynamic nodes are traversed during reconciliation
Incorrect:
<script setup>
import { h, ref } from 'vue'
const count = ref(0)
const items = ref(['a', 'b', 'c'])
// BAD: Hand-written render function - no compile-time optimizations
// Every render: all vnodes created anew, all nodes diffed
const render = () => h('div', [
h('header', { class: 'static-header' }, 'My App'), // Static but recreated every render
h('p', `Count: ${count.value}`),
h('ul', items.value.map(item => h('li', { key: item }, item)))
])
</script>
Correct:
<template>
<div>
<!-- GOOD: Static header hoisted, never recreated or diffed -->
<header class="static-header">My App</header>
<!-- GOOD: Patch flag marks only text as dynamic -->
<p>Count: {{ count }}</p>
<!-- GOOD: Only list items are tracked as dynamic -->
<ul>
<li v-for="item in items" :key="item">{{ item }}</li>
</ul>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const items = ref(['a', 'b', 'c'])
</script>
When Render Functions Are Appropriate
Use render functions when:
- Rendering logic is highly dynamic (dynamic component types based on data)
- Building reusable library components with complex slot forwarding
- Need programmatic control that templates cannot express
// ACCEPTABLE: Dynamic component type based on data
function render() {
return h(props.level > 2 ? 'h3' : 'h' + props.level, slots.default?.())
}
// ACCEPTABLE: Complex library component with dynamic children manipulation
function render() {
const children = slots.default?.() || []
return h('div', children.filter(shouldRender).map(wrapChild))
}
Compiler Optimizations You Lose
| Optimization | With Templates | With Render Functions |
|---|---|---|
| Static Hoisting | Automatic | Manual (you must do it) |
| Patch Flags | Automatic | None |
| Tree Flattening | Automatic | None |
| SSR Hydration Optimization | Full | Partial |
| Block-level fast paths | Yes | No |
If You Must Use Render Functions
import { h, ref } from 'vue'
// Manually hoist static vnodes outside component
const staticHeader = h('header', { class: 'static' }, 'Title')
export default {
setup() {
const count = ref(0)
return () => h('div', [
staticHeader, // Reused, not recreated
h('p', `Count: ${count.value}`)
])
}
}