- 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.3 KiB
5.3 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Implement getSSRProps for Custom Directives in SSR | MEDIUM | Custom directives without SSR handling cause hydration mismatches or missing functionality | best-practice |
|
Implement getSSRProps for Custom Directives in SSR
Impact: MEDIUM - Custom directives only have access to the DOM on the client side. During SSR, the directive's mounted and updated hooks never run. If your directive sets attributes or modifies the element, you must implement getSSRProps to return equivalent attributes for server rendering.
Without getSSRProps, the server-rendered HTML won't include the directive's effects, causing hydration mismatches when the client applies the directive.
Task Checklist
- Add
getSSRPropshook to directives that modify element attributes - Return an object with HTML attributes to render on server
- Test directive behavior in both SSR and client-only contexts
- Consider using components instead of directives for complex SSR cases
Incorrect - Client-Only Directive:
// WRONG: No SSR handling - directive effects missing on server
const vTooltip = {
mounted(el, binding) {
el.setAttribute('data-tooltip', binding.value)
el.setAttribute('aria-label', binding.value)
el.classList.add('has-tooltip')
}
}
Server renders:
<!-- Missing data-tooltip, aria-label, and has-tooltip class -->
<button>Hover me</button>
Client after hydration:
<!-- Directive applies, but causes mismatch -->
<button data-tooltip="Help text" aria-label="Help text" class="has-tooltip">
Hover me
</button>
Correct - With getSSRProps:
// CORRECT: SSR-compatible directive
const vTooltip = {
// Client-side implementation
mounted(el, binding) {
el.setAttribute('data-tooltip', binding.value)
el.setAttribute('aria-label', binding.value)
el.classList.add('has-tooltip')
},
// SSR implementation - returns attributes to render
getSSRProps(binding) {
return {
'data-tooltip': binding.value,
'aria-label': binding.value,
class: 'has-tooltip'
}
}
}
Server now renders:
<button data-tooltip="Help text" aria-label="Help text" class="has-tooltip">
Hover me
</button>
Complete SSR Directive Example
// directives/vFocus.js
export const vFocus = {
// Client: Actually focus the element
mounted(el, binding) {
if (binding.value !== false) {
el.focus()
}
},
// SSR: Add autofocus attribute so browser focuses on load
getSSRProps(binding) {
if (binding.value !== false) {
return { autofocus: true }
}
return {}
}
}
<template>
<input v-focus type="text" placeholder="Auto-focused input" />
</template>
<script setup>
import { vFocus } from '@/directives/vFocus'
</script>
Directive with Dynamic ID
// CORRECT: Generate consistent IDs
const vId = {
mounted(el, binding) {
el.id = binding.value || `el-${binding.instance?.$.uid}`
},
getSSRProps(binding, vnode) {
// Use the same ID generation logic
return {
id: binding.value || `el-${vnode.component?.uid || 'ssr'}`
}
}
}
Handling Complex Directives
For directives that do more than set attributes, consider:
// Directive that only makes sense on client (e.g., drag-and-drop)
const vDraggable = {
mounted(el, binding) {
// Complex client-side logic
initDragAndDrop(el, binding.value)
},
unmounted(el) {
destroyDragAndDrop(el)
},
// SSR: Just mark element as draggable for styling/semantics
getSSRProps(binding) {
return {
draggable: 'true',
'data-draggable': '',
role: 'listitem'
}
}
}
Directives That Cannot Have SSR Equivalents
Some directives have no meaningful server-side representation:
// Directive that tracks mouse position - no SSR equivalent
const vMousePosition = {
mounted(el, binding) {
el.addEventListener('mousemove', (e) => {
binding.value?.(e.clientX, e.clientY)
})
},
// Nothing meaningful to render on server
getSSRProps() {
return {} // Empty object - no attributes
}
}
Nuxt.js Directive Registration
// plugins/directives.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.directive('tooltip', {
mounted(el, binding) {
el.setAttribute('data-tooltip', binding.value)
},
getSSRProps(binding) {
return { 'data-tooltip': binding.value }
}
})
})
Testing SSR Directives
import { renderToString } from 'vue/server-renderer'
import { createSSRApp, h } from 'vue'
import { vTooltip } from './directives/vTooltip'
test('vTooltip renders attributes during SSR', async () => {
const app = createSSRApp({
directives: { tooltip: vTooltip },
template: '<button v-tooltip="\'Help text\'">Click</button>'
})
const html = await renderToString(app)
expect(html).toContain('data-tooltip="Help text"')
expect(html).toContain('aria-label="Help text"')
})