12451 b553c4e519 feat: API 统一 + 设备详情补全 + H5 导航优化
- 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: 下拉刷新 / 上拉加载
- 产品首页: 添加业务联系
- 文档: 清理过时文件,补充缺失规范章节
2026-07-04 04:03:02 +08:00

4.6 KiB

title, impact, impactDescription, type, tags
title impact impactDescription type tags
Use .once Modifier for Single-Use Event Handlers LOW The .once modifier auto-removes event listeners after first trigger, preventing repeated handler calls best-practice
vue3
events
modifiers
once
event-handling

Use .once Modifier for Single-Use Event Handlers

Impact: LOW - Vue provides a .once modifier for event listeners that automatically removes the listener after it fires once. This is useful for one-time events like initialization callbacks, first-interaction tracking, or one-time animations.

Task Checklist

  • Use .once for events that should only fire once
  • Consider .once for analytics first-interaction tracking
  • Use .once for initialization events
  • Remember .once works on both native and component events

Basic Usage

Component events:

<template>
  <!-- Handler fires only on first emit, then stops listening -->
  <ChildComponent @initialized.once="handleInit" />

  <!-- First-time user interaction tracking -->
  <UserForm @submit.once="trackFirstSubmit" />
</template>

<script setup>
function handleInit(data) {
  console.log('Component initialized:', data)
  // This only runs once, even if child emits 'initialized' multiple times
}

function trackFirstSubmit() {
  analytics.track('first_form_submit')
}
</script>

Native DOM events:

<template>
  <!-- First click only -->
  <button @click.once="showWelcomeMessage">
    Click to see welcome (once)
  </button>

  <!-- First scroll only -->
  <div @scroll.once="loadMoreContent">
    Scroll to load more
  </div>
</template>

Common Use Cases

One-Time Initialization

<template>
  <ThirdPartyChart
    @ready.once="onChartReady"
    :data="chartData"
  />
</template>

<script setup>
function onChartReady(chartInstance) {
  // Store reference, configure chart
  // Only need to do this once
  chartRef.value = chartInstance
  chartInstance.setOption(customOptions)
}
</script>

First Interaction Analytics

<template>
  <article @click.once="trackEngagement">
    <h2>{{ article.title }}</h2>
    <p>{{ article.excerpt }}</p>
  </article>
</template>

<script setup>
function trackEngagement() {
  analytics.track('article_first_click', {
    articleId: article.id
  })
}
</script>

Lazy Loading Trigger

<template>
  <div
    ref="lazyContainer"
    @mouseenter.once="loadHeavyContent"
  >
    <template v-if="loaded">
      <HeavyComponent :data="data" />
    </template>
    <template v-else>
      <Placeholder />
    </template>
  </div>
</template>

<script setup>
const loaded = ref(false)
const data = ref(null)

async function loadHeavyContent() {
  data.value = await fetchData()
  loaded.value = true
}
</script>

One-Time Animation

<template>
  <Transition
    @after-enter.once="onFirstAppearance"
  >
    <div v-if="show" class="animated-content">
      Content
    </div>
  </Transition>
</template>

<script setup>
function onFirstAppearance() {
  // Track first time user sees this element
  // Won't fire again if element leaves and re-enters
}
</script>

Combining with Other Modifiers

<template>
  <!-- Once + prevent default -->
  <form @submit.once.prevent="handleFirstSubmit">
    ...
  </form>

  <!-- Once + stop propagation -->
  <div @click.once.stop="handleClick">
    ...
  </div>

  <!-- Once + key modifier -->
  <input @keyup.enter.once="submitOnFirstEnter" />
</template>

Equivalent Manual Implementation

Without .once, you'd need to manually track and remove:

<script setup>
const hasHandled = ref(false)

function handleClickManually() {
  if (hasHandled.value) return
  hasHandled.value = true

  // Do one-time action
  doSomething()
}
</script>

<template>
  <!-- Manual approach - more verbose -->
  <button @click="handleClickManually">Click</button>

  <!-- .once approach - cleaner -->
  <button @click.once="doSomething">Click</button>
</template>

When NOT to Use .once

Don't use .once when:

  • You need the event to fire multiple times
  • You want to conditionally allow repeated fires
  • The "once" logic is complex (use manual ref tracking instead)
<template>
  <!-- DON'T: User expects multiple submissions to work -->
  <form @submit.once.prevent="handleSubmit">
    ...
  </form>

  <!-- DO: Allow repeated submissions -->
  <form @submit.prevent="handleSubmit">
    ...
  </form>
</template>

Reference