Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
214 lines
4.6 KiB
Markdown
214 lines
4.6 KiB
Markdown
---
|
|
title: Use .once Modifier for Single-Use Event Handlers
|
|
impact: LOW
|
|
impactDescription: The .once modifier auto-removes event listeners after first trigger, preventing repeated handler calls
|
|
type: best-practice
|
|
tags: [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:**
|
|
```vue
|
|
<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:**
|
|
```vue
|
|
<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
|
|
```vue
|
|
<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
|
|
```vue
|
|
<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
|
|
```vue
|
|
<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
|
|
```vue
|
|
<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
|
|
|
|
```vue
|
|
<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:
|
|
|
|
```vue
|
|
<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)
|
|
|
|
```vue
|
|
<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
|
|
- [Vue.js Event Handling - Event Modifiers](https://vuejs.org/guide/essentials/event-handling.html#event-modifiers)
|
|
- [Vue.js Component Events](https://vuejs.org/guide/components/events.html)
|