@pythoughts/vue-skills-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/README.md +63 -0
  2. package/index.mjs +139 -0
  3. package/package.json +34 -0
  4. package/skills/create-adaptable-composable/SKILL.md +76 -0
  5. package/skills/vue-best-practices/SKILL.md +154 -0
  6. package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
  7. package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
  8. package/skills/vue-best-practices/references/component-async.md +97 -0
  9. package/skills/vue-best-practices/references/component-data-flow.md +350 -0
  10. package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
  11. package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
  12. package/skills/vue-best-practices/references/component-slots.md +216 -0
  13. package/skills/vue-best-practices/references/component-suspense.md +228 -0
  14. package/skills/vue-best-practices/references/component-teleport.md +108 -0
  15. package/skills/vue-best-practices/references/component-transition-group.md +128 -0
  16. package/skills/vue-best-practices/references/component-transition.md +125 -0
  17. package/skills/vue-best-practices/references/composables.md +290 -0
  18. package/skills/vue-best-practices/references/directives.md +162 -0
  19. package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
  20. package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
  21. package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
  22. package/skills/vue-best-practices/references/plugins.md +166 -0
  23. package/skills/vue-best-practices/references/reactivity.md +346 -0
  24. package/skills/vue-best-practices/references/render-functions.md +201 -0
  25. package/skills/vue-best-practices/references/sfc.md +310 -0
  26. package/skills/vue-best-practices/references/state-management.md +135 -0
  27. package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
  28. package/skills/vue-debug-guides/SKILL.md +205 -0
  29. package/skills/vue-debug-guides/reference/animation-key-for-rerender.md +160 -0
  30. package/skills/vue-debug-guides/reference/animation-transitiongroup-performance.md +241 -0
  31. package/skills/vue-debug-guides/reference/async-component-error-handling.md +115 -0
  32. package/skills/vue-debug-guides/reference/async-component-keepalive-ref-issue.md +112 -0
  33. package/skills/vue-debug-guides/reference/async-component-suspense-control.md +84 -0
  34. package/skills/vue-debug-guides/reference/async-component-vue-router.md +109 -0
  35. package/skills/vue-debug-guides/reference/attrs-event-listener-merging.md +205 -0
  36. package/skills/vue-debug-guides/reference/checkbox-true-false-value-form-submission.md +118 -0
  37. package/skills/vue-debug-guides/reference/cleanup-side-effects.md +172 -0
  38. package/skills/vue-debug-guides/reference/click-events-on-components.md +180 -0
  39. package/skills/vue-debug-guides/reference/component-naming-conflicts.md +159 -0
  40. package/skills/vue-debug-guides/reference/component-ref-requires-defineexpose.md +176 -0
  41. package/skills/vue-debug-guides/reference/composable-avoid-hidden-side-effects.md +208 -0
  42. package/skills/vue-debug-guides/reference/composable-call-location-restrictions.md +141 -0
  43. package/skills/vue-debug-guides/reference/composable-naming-return-pattern.md +139 -0
  44. package/skills/vue-debug-guides/reference/composable-tovalue-inside-watcheffect.md +182 -0
  45. package/skills/vue-debug-guides/reference/composition-api-not-functional-programming.md +120 -0
  46. package/skills/vue-debug-guides/reference/composition-api-script-setup-async-context.md +203 -0
  47. package/skills/vue-debug-guides/reference/composition-api-vs-react-hooks-differences.md +156 -0
  48. package/skills/vue-debug-guides/reference/computed-array-mutation.md +148 -0
  49. package/skills/vue-debug-guides/reference/computed-conditional-dependencies.md +147 -0
  50. package/skills/vue-debug-guides/reference/computed-no-parameters.md +159 -0
  51. package/skills/vue-debug-guides/reference/computed-no-side-effects.md +107 -0
  52. package/skills/vue-debug-guides/reference/computed-return-value-readonly.md +160 -0
  53. package/skills/vue-debug-guides/reference/configure-app-before-mount.md +89 -0
  54. package/skills/vue-debug-guides/reference/declare-emits-for-documentation.md +212 -0
  55. package/skills/vue-debug-guides/reference/define-expose-before-await.md +192 -0
  56. package/skills/vue-debug-guides/reference/define-model-default-value-sync.md +139 -0
  57. package/skills/vue-debug-guides/reference/defineEmits-must-be-top-level.md +164 -0
  58. package/skills/vue-debug-guides/reference/defineEmits-no-runtime-and-type-mixed.md +170 -0
  59. package/skills/vue-debug-guides/reference/definemodel-object-mutation-no-emit.md +148 -0
  60. package/skills/vue-debug-guides/reference/dom-update-timing-nexttick.md +90 -0
  61. package/skills/vue-debug-guides/reference/dynamic-argument-constraints.md +146 -0
  62. package/skills/vue-debug-guides/reference/dynamic-component-registration-vite.md +147 -0
  63. package/skills/vue-debug-guides/reference/event-modifier-order-matters.md +101 -0
  64. package/skills/vue-debug-guides/reference/exact-modifier-for-precise-shortcuts.md +155 -0
  65. package/skills/vue-debug-guides/reference/fallthrough-attrs-overwrite-vue3.md +159 -0
  66. package/skills/vue-debug-guides/reference/in-dom-template-parsing-caveats.md +149 -0
  67. package/skills/vue-debug-guides/reference/inheritattrs-false-for-wrapper-components.md +230 -0
  68. package/skills/vue-debug-guides/reference/keepalive-router-nested-double-mount.md +222 -0
  69. package/skills/vue-debug-guides/reference/keepalive-transition-memory-leak.md +144 -0
  70. package/skills/vue-debug-guides/reference/keyup-modifier-timing.md +137 -0
  71. package/skills/vue-debug-guides/reference/lifecycle-dom-access-timing.md +216 -0
  72. package/skills/vue-debug-guides/reference/lifecycle-hooks-synchronous-registration.md +156 -0
  73. package/skills/vue-debug-guides/reference/lifecycle-ssr-awareness.md +184 -0
  74. package/skills/vue-debug-guides/reference/local-components-not-in-descendants.md +151 -0
  75. package/skills/vue-debug-guides/reference/mount-return-value.md +88 -0
  76. package/skills/vue-debug-guides/reference/multi-root-component-class-attrs.md +93 -0
  77. package/skills/vue-debug-guides/reference/native-event-collision-with-emits.md +162 -0
  78. package/skills/vue-debug-guides/reference/no-passive-with-prevent.md +141 -0
  79. package/skills/vue-debug-guides/reference/no-v-if-with-v-for.md +136 -0
  80. package/skills/vue-debug-guides/reference/perf-computed-object-stability.md +157 -0
  81. package/skills/vue-debug-guides/reference/perf-props-stability-update-optimization.md +140 -0
  82. package/skills/vue-debug-guides/reference/plugin-global-properties-sparingly.md +109 -0
  83. package/skills/vue-debug-guides/reference/plugin-install-before-mount.md +124 -0
  84. package/skills/vue-debug-guides/reference/plugin-prefer-provide-inject-over-global-properties.md +120 -0
  85. package/skills/vue-debug-guides/reference/plugin-typescript-type-augmentation.md +157 -0
  86. package/skills/vue-debug-guides/reference/prop-defineprops-scope-limitation.md +161 -0
  87. package/skills/vue-debug-guides/reference/provide-inject-debugging-challenges.md +203 -0
  88. package/skills/vue-debug-guides/reference/provide-inject-default-value-factory.md +244 -0
  89. package/skills/vue-debug-guides/reference/provide-inject-reactivity-not-automatic.md +226 -0
  90. package/skills/vue-debug-guides/reference/provide-inject-synchronous-setup.md +235 -0
  91. package/skills/vue-debug-guides/reference/reactive-destructuring.md +89 -0
  92. package/skills/vue-debug-guides/reference/reactivity-debugging-hooks.md +132 -0
  93. package/skills/vue-debug-guides/reference/reactivity-markraw-for-non-reactive.md +149 -0
  94. package/skills/vue-debug-guides/reference/reactivity-proxy-identity-hazard.md +96 -0
  95. package/skills/vue-debug-guides/reference/reactivity-same-tick-batching.md +166 -0
  96. package/skills/vue-debug-guides/reference/ref-value-access.md +61 -0
  97. package/skills/vue-debug-guides/reference/refs-in-collections-need-value.md +81 -0
  98. package/skills/vue-debug-guides/reference/render-function-avoid-internal-vnode-properties.md +151 -0
  99. package/skills/vue-debug-guides/reference/render-function-vnodes-must-be-unique.md +133 -0
  100. package/skills/vue-debug-guides/reference/rendering-render-function-h-import-vue3.md +148 -0
  101. package/skills/vue-debug-guides/reference/rendering-render-function-return-from-setup.md +148 -0
  102. package/skills/vue-debug-guides/reference/rendering-render-function-slots-as-functions.md +168 -0
  103. package/skills/vue-debug-guides/reference/rendering-resolve-component-for-string-names.md +231 -0
  104. package/skills/vue-debug-guides/reference/select-initial-value-ios-bug.md +91 -0
  105. package/skills/vue-debug-guides/reference/self-referencing-component-name.md +157 -0
  106. package/skills/vue-debug-guides/reference/sfc-named-exports-forbidden.md +184 -0
  107. package/skills/vue-debug-guides/reference/sfc-scoped-css-child-component-styling.md +156 -0
  108. package/skills/vue-debug-guides/reference/sfc-scoped-css-dynamic-content.md +193 -0
  109. package/skills/vue-debug-guides/reference/sfc-scoped-css-slot-content.md +242 -0
  110. package/skills/vue-debug-guides/reference/sfc-script-setup-reactivity.md +195 -0
  111. package/skills/vue-debug-guides/reference/slot-forwarding-to-child-components.md +143 -0
  112. package/skills/vue-debug-guides/reference/slot-implicit-default-content.md +155 -0
  113. package/skills/vue-debug-guides/reference/slot-name-reserved-prop.md +109 -0
  114. package/skills/vue-debug-guides/reference/slot-named-scoped-explicit-default.md +95 -0
  115. package/skills/vue-debug-guides/reference/slot-render-scope-parent-only.md +135 -0
  116. package/skills/vue-debug-guides/reference/slot-v-slot-on-components-or-templates-only.md +122 -0
  117. package/skills/vue-debug-guides/reference/ssr-hydration-mismatch-causes.md +280 -0
  118. package/skills/vue-debug-guides/reference/ssr-platform-specific-apis.md +256 -0
  119. package/skills/vue-debug-guides/reference/state-ssr-cross-request-pollution.md +276 -0
  120. package/skills/vue-debug-guides/reference/suspense-no-builtin-error-handling.md +127 -0
  121. package/skills/vue-debug-guides/reference/suspense-ssr-hydration-issues.md +159 -0
  122. package/skills/vue-debug-guides/reference/tailwind-dynamic-class-generation.md +144 -0
  123. package/skills/vue-debug-guides/reference/teleport-scoped-styles-limitation.md +191 -0
  124. package/skills/vue-debug-guides/reference/teleport-ssr-hydration.md +152 -0
  125. package/skills/vue-debug-guides/reference/teleport-target-must-exist.md +113 -0
  126. package/skills/vue-debug-guides/reference/template-expressions-restrictions.md +114 -0
  127. package/skills/vue-debug-guides/reference/template-functions-no-side-effects.md +187 -0
  128. package/skills/vue-debug-guides/reference/template-ref-null-with-v-if.md +123 -0
  129. package/skills/vue-debug-guides/reference/template-ref-unwrapping-top-level.md +104 -0
  130. package/skills/vue-debug-guides/reference/template-ref-v-for-order.md +172 -0
  131. package/skills/vue-debug-guides/reference/textarea-no-interpolation.md +72 -0
  132. package/skills/vue-debug-guides/reference/transition-group-flip-inline-elements.md +152 -0
  133. package/skills/vue-debug-guides/reference/transition-group-move-animation-position-absolute.md +130 -0
  134. package/skills/vue-debug-guides/reference/transition-group-no-default-wrapper-vue3.md +152 -0
  135. package/skills/vue-debug-guides/reference/transition-js-hooks-done-callback.md +251 -0
  136. package/skills/vue-debug-guides/reference/transition-nested-duration.md +182 -0
  137. package/skills/vue-debug-guides/reference/transition-reusable-scoped-style.md +245 -0
  138. package/skills/vue-debug-guides/reference/transition-router-view-appear.md +193 -0
  139. package/skills/vue-debug-guides/reference/transition-type-when-mixed.md +172 -0
  140. package/skills/vue-debug-guides/reference/transition-unmount-hook-timing.md +149 -0
  141. package/skills/vue-debug-guides/reference/ts-defineprops-boolean-default-false.md +225 -0
  142. package/skills/vue-debug-guides/reference/ts-defineprops-imported-types-limitations.md +281 -0
  143. package/skills/vue-debug-guides/reference/ts-event-handler-explicit-typing.md +213 -0
  144. package/skills/vue-debug-guides/reference/ts-reactive-no-generic-argument.md +196 -0
  145. package/skills/vue-debug-guides/reference/ts-shallowref-for-dynamic-components.md +218 -0
  146. package/skills/vue-debug-guides/reference/ts-template-ref-null-handling.md +249 -0
  147. package/skills/vue-debug-guides/reference/ts-template-type-casting.md +214 -0
  148. package/skills/vue-debug-guides/reference/ts-withdefaults-mutable-factory-function.md +171 -0
  149. package/skills/vue-debug-guides/reference/undeclared-emits-double-firing.md +195 -0
  150. package/skills/vue-debug-guides/reference/use-template-ref-vue35.md +158 -0
  151. package/skills/vue-debug-guides/reference/v-else-must-follow-v-if.md +136 -0
  152. package/skills/vue-debug-guides/reference/v-for-component-props.md +95 -0
  153. package/skills/vue-debug-guides/reference/v-for-computed-reverse-sort.md +86 -0
  154. package/skills/vue-debug-guides/reference/v-for-key-attribute.md +90 -0
  155. package/skills/vue-debug-guides/reference/v-for-range-starts-at-one.md +66 -0
  156. package/skills/vue-debug-guides/reference/v-if-null-check-order.md +171 -0
  157. package/skills/vue-debug-guides/reference/v-model-ignores-html-attributes.md +83 -0
  158. package/skills/vue-debug-guides/reference/v-model-ime-composition.md +83 -0
  159. package/skills/vue-debug-guides/reference/v-model-number-modifier-behavior.md +124 -0
  160. package/skills/vue-debug-guides/reference/v-show-template-limitation.md +124 -0
  161. package/skills/vue-debug-guides/reference/watch-async-cleanup.md +180 -0
  162. package/skills/vue-debug-guides/reference/watch-async-creation-memory-leak.md +176 -0
  163. package/skills/vue-debug-guides/reference/watch-deep-same-object-reference.md +165 -0
  164. package/skills/vue-debug-guides/reference/watch-flush-timing.md +189 -0
  165. package/skills/vue-debug-guides/reference/watch-reactive-property-getter.md +108 -0
  166. package/skills/vue-debug-guides/reference/watcheffect-async-dependency-tracking.md +173 -0
  167. package/skills/vue-debug-guides/reference/watcheffect-flush-post-for-refs.md +176 -0
  168. package/skills/vue-jsx-best-practices/SKILL.md +12 -0
  169. package/skills/vue-jsx-best-practices/reference/render-function-jsx-vue-vs-react.md +141 -0
  170. package/skills/vue-options-api-best-practices/SKILL.md +23 -0
  171. package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-lifecycle-hooks.md +95 -0
  172. package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-methods.md +68 -0
  173. package/skills/vue-options-api-best-practices/reference/stateful-methods-lifecycle.md +61 -0
  174. package/skills/vue-options-api-best-practices/reference/ts-options-api-arrow-functions-validators.md +141 -0
  175. package/skills/vue-options-api-best-practices/reference/ts-options-api-computed-return-types.md +192 -0
  176. package/skills/vue-options-api-best-practices/reference/ts-options-api-proptype-complex-types.md +212 -0
  177. package/skills/vue-options-api-best-practices/reference/ts-options-api-provide-inject-limitations.md +135 -0
  178. package/skills/vue-options-api-best-practices/reference/ts-options-api-type-event-handlers.md +202 -0
  179. package/skills/vue-options-api-best-practices/reference/ts-options-api-use-definecomponent.md +172 -0
  180. package/skills/vue-options-api-best-practices/reference/ts-strict-mode-options-api.md +197 -0
  181. package/skills/vue-pinia-best-practices/SKILL.md +21 -0
  182. package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
  183. package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
  184. package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
  185. package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
  186. package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
  187. package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
  188. package/skills/vue-router-best-practices/SKILL.md +23 -0
  189. package/skills/vue-router-best-practices/reference/router-beforeenter-no-param-trigger.md +167 -0
  190. package/skills/vue-router-best-practices/reference/router-beforerouteenter-no-this.md +176 -0
  191. package/skills/vue-router-best-practices/reference/router-guard-async-await-pattern.md +227 -0
  192. package/skills/vue-router-best-practices/reference/router-navigation-guard-infinite-loop.md +187 -0
  193. package/skills/vue-router-best-practices/reference/router-navigation-guard-next-deprecated.md +150 -0
  194. package/skills/vue-router-best-practices/reference/router-param-change-no-lifecycle.md +181 -0
  195. package/skills/vue-router-best-practices/reference/router-simple-routing-cleanup.md +209 -0
  196. package/skills/vue-router-best-practices/reference/router-use-vue-router-for-production.md +183 -0
  197. package/skills/vue-testing-best-practices/SKILL.md +29 -0
  198. package/skills/vue-testing-best-practices/reference/async-component-testing.md +163 -0
  199. package/skills/vue-testing-best-practices/reference/teleport-testing-complexity.md +158 -0
  200. package/skills/vue-testing-best-practices/reference/testing-async-await-flushpromises.md +175 -0
  201. package/skills/vue-testing-best-practices/reference/testing-browser-vs-node-runners.md +208 -0
  202. package/skills/vue-testing-best-practices/reference/testing-component-blackbox-approach.md +144 -0
  203. package/skills/vue-testing-best-practices/reference/testing-composables-helper-wrapper.md +238 -0
  204. package/skills/vue-testing-best-practices/reference/testing-e2e-playwright-recommended.md +242 -0
  205. package/skills/vue-testing-best-practices/reference/testing-no-snapshot-only.md +197 -0
  206. package/skills/vue-testing-best-practices/reference/testing-pinia-store-setup.md +228 -0
  207. package/skills/vue-testing-best-practices/reference/testing-suspense-async-components.md +229 -0
  208. package/skills/vue-testing-best-practices/reference/testing-vitest-recommended-for-vue.md +204 -0
@@ -0,0 +1,187 @@
1
+ ---
2
+ title: Template Functions Must Be Pure Without Side Effects
3
+ impact: MEDIUM
4
+ impactDescription: Functions with side effects in templates cause unpredictable behavior on every re-render
5
+ type: efficiency
6
+ tags: [vue3, template, functions, performance, side-effects]
7
+ ---
8
+
9
+ # Template Functions Must Be Pure Without Side Effects
10
+
11
+ **Impact: MEDIUM** - Functions called in templates execute on every component re-render. Functions with side effects (modifying data, API calls, logging) will cause unpredictable behavior, performance issues, and difficult-to-debug bugs.
12
+
13
+ Template expressions including function calls are evaluated whenever the component updates. This makes them unsuitable for operations that should only happen once or that modify state.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Keep template functions pure (same input = same output)
18
+ - [ ] Never modify reactive state inside template functions
19
+ - [ ] Never make API calls or async operations in template functions
20
+ - [ ] Move side effects to event handlers, watchers, or lifecycle hooks
21
+ - [ ] Use computed properties for derived values instead of functions when possible
22
+ - [ ] Avoid expensive computations; use computed properties for caching
23
+
24
+ **Incorrect:**
25
+ ```vue
26
+ <template>
27
+ <!-- BAD: Modifies state on every render -->
28
+ <p>{{ incrementAndGet() }}</p>
29
+
30
+ <!-- BAD: API call on every render -->
31
+ <div>{{ fetchUserName() }}</div>
32
+
33
+ <!-- BAD: Logging side effect -->
34
+ <span>{{ logAndFormat(date) }}</span>
35
+
36
+ <!-- BAD: Expensive computation without caching -->
37
+ <ul>
38
+ <li v-for="item in filterAndSort(items)" :key="item.id">
39
+ {{ item.name }}
40
+ </li>
41
+ </ul>
42
+
43
+ <!-- BAD: Random values change on every render -->
44
+ <p>{{ getRandomGreeting() }}</p>
45
+ </template>
46
+
47
+ <script setup>
48
+ import { ref } from 'vue'
49
+
50
+ const count = ref(0)
51
+ const items = ref([/* large array */])
52
+
53
+ // BAD: Has side effect - modifies state
54
+ function incrementAndGet() {
55
+ count.value++ // Side effect!
56
+ return count.value
57
+ }
58
+
59
+ // BAD: Async operation in template
60
+ async function fetchUserName() {
61
+ const res = await fetch('/api/user') // Side effect!
62
+ return (await res.json()).name
63
+ }
64
+
65
+ // BAD: Logging is a side effect
66
+ function logAndFormat(date) {
67
+ console.log('Formatting date:', date) // Side effect!
68
+ return new Date(date).toLocaleDateString()
69
+ }
70
+
71
+ // BAD: Expensive, runs every render without caching
72
+ function filterAndSort(items) {
73
+ return items
74
+ .filter(i => i.active)
75
+ .sort((a, b) => a.name.localeCompare(b.name))
76
+ }
77
+
78
+ // BAD: Non-deterministic
79
+ function getRandomGreeting() {
80
+ const greetings = ['Hello', 'Hi', 'Hey']
81
+ return greetings[Math.floor(Math.random() * greetings.length)]
82
+ }
83
+ </script>
84
+ ```
85
+
86
+ **Correct:**
87
+ ```vue
88
+ <template>
89
+ <!-- OK: Pure formatting function -->
90
+ <p>Count: {{ count }}</p>
91
+ <button @click="increment">Increment</button>
92
+
93
+ <!-- OK: Data fetched via lifecycle/watcher -->
94
+ <div>{{ userName }}</div>
95
+
96
+ <!-- OK: Pure function, no side effects -->
97
+ <span>{{ formatDate(date) }}</span>
98
+
99
+ <!-- OK: Computed property caches result -->
100
+ <ul>
101
+ <li v-for="item in filteredAndSortedItems" :key="item.id">
102
+ {{ item.name }}
103
+ </li>
104
+ </ul>
105
+
106
+ <!-- OK: Random value set once -->
107
+ <p>{{ greeting }}</p>
108
+ </template>
109
+
110
+ <script setup>
111
+ import { ref, computed, onMounted } from 'vue'
112
+
113
+ const count = ref(0)
114
+ const userName = ref('')
115
+ const date = ref(new Date())
116
+ const items = ref([/* large array */])
117
+
118
+ // Side effects in event handlers
119
+ function increment() {
120
+ count.value++
121
+ }
122
+
123
+ // Fetch data in lifecycle hook
124
+ onMounted(async () => {
125
+ const res = await fetch('/api/user')
126
+ userName.value = (await res.json()).name
127
+ })
128
+
129
+ // Pure function - same input, same output
130
+ function formatDate(date) {
131
+ return new Date(date).toLocaleDateString()
132
+ }
133
+
134
+ // Computed property - cached, only recalculates when dependencies change
135
+ const filteredAndSortedItems = computed(() => {
136
+ return items.value
137
+ .filter(i => i.active)
138
+ .sort((a, b) => a.name.localeCompare(b.name))
139
+ })
140
+
141
+ // Set random value once, not on every render
142
+ const greetings = ['Hello', 'Hi', 'Hey']
143
+ const greeting = ref(greetings[Math.floor(Math.random() * greetings.length)])
144
+ </script>
145
+ ```
146
+
147
+ ## Pure Function Guidelines
148
+
149
+ A pure function:
150
+ 1. Given the same inputs, always returns the same output
151
+ 2. Does not modify any external state
152
+ 3. Does not perform I/O operations (network, console, file system)
153
+ 4. Does not depend on mutable external state
154
+
155
+ ```javascript
156
+ // PURE - safe for templates
157
+ function formatCurrency(amount, currency = 'USD') {
158
+ return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)
159
+ }
160
+
161
+ function fullName(first, last) {
162
+ return `${first} ${last}`
163
+ }
164
+
165
+ function isExpired(date) {
166
+ return new Date(date) < new Date()
167
+ }
168
+
169
+ // IMPURE - unsafe for templates
170
+ function logAndReturn(value) {
171
+ console.log(value) // I/O
172
+ return value
173
+ }
174
+
175
+ function getFromLocalStorage(key) {
176
+ return localStorage.getItem(key) // External state
177
+ }
178
+
179
+ function updateAndReturn(obj, key, value) {
180
+ obj[key] = value // Mutation
181
+ return obj
182
+ }
183
+ ```
184
+
185
+ ## Reference
186
+ - [Vue.js Template Syntax - Calling Functions](https://vuejs.org/guide/essentials/template-syntax.html#calling-functions)
187
+ - [Vue.js Computed Properties](https://vuejs.org/guide/essentials/computed.html)
@@ -0,0 +1,123 @@
1
+ ---
2
+ title: Template Refs Become Null When Elements Are Unmounted
3
+ impact: MEDIUM
4
+ impactDescription: Refs become null when conditionally rendered elements are removed, causing errors if not handled
5
+ type: gotcha
6
+ tags: [vue3, template-refs, v-if, watchers, conditional-rendering]
7
+ ---
8
+
9
+ # Template Refs Become Null When Elements Are Unmounted
10
+
11
+ **Impact: MEDIUM** - When using template refs with `v-if`, the ref becomes `null` when the element is unmounted. Watchers and effects that access these refs must handle the null case to avoid runtime errors.
12
+
13
+ This is especially tricky with `watchEffect` since it runs automatically and may execute when the ref is null.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Always check for null before accessing ref properties when using v-if
18
+ - [ ] In watchers, explicitly handle the null case (element unmounted or not yet mounted)
19
+ - [ ] Consider whether v-show is more appropriate if you need persistent ref access
20
+ - [ ] Use optional chaining (?.) when accessing ref properties in uncertain contexts
21
+
22
+ **Incorrect:**
23
+ ```vue
24
+ <script setup>
25
+ import { ref, watchEffect } from 'vue'
26
+
27
+ const inputEl = ref(null)
28
+ const showInput = ref(true)
29
+
30
+ // WRONG: No null check - will error when v-if is false
31
+ watchEffect(() => {
32
+ inputEl.value.focus() // TypeError when showInput is false
33
+ })
34
+ </script>
35
+
36
+ <template>
37
+ <input v-if="showInput" ref="inputEl" />
38
+ <button @click="showInput = !showInput">Toggle</button>
39
+ </template>
40
+ ```
41
+
42
+ **Correct:**
43
+ ```vue
44
+ <script setup>
45
+ import { ref, watchEffect } from 'vue'
46
+
47
+ const inputEl = ref(null)
48
+ const showInput = ref(true)
49
+
50
+ // CORRECT: Handle both mounted and unmounted states
51
+ watchEffect(() => {
52
+ if (inputEl.value) {
53
+ inputEl.value.focus()
54
+ } else {
55
+ // Element not mounted yet, or unmounted by v-if
56
+ console.log('Input element not available')
57
+ }
58
+ })
59
+ </script>
60
+
61
+ <template>
62
+ <input v-if="showInput" ref="inputEl" />
63
+ <button @click="showInput = !showInput">Toggle</button>
64
+ </template>
65
+ ```
66
+
67
+ ```vue
68
+ <script setup>
69
+ import { ref, watch } from 'vue'
70
+
71
+ const inputEl = ref(null)
72
+ const showInput = ref(true)
73
+
74
+ // CORRECT: Watch the ref and handle null explicitly
75
+ watch(inputEl, (el) => {
76
+ if (el) {
77
+ el.focus()
78
+ }
79
+ })
80
+ </script>
81
+
82
+ <template>
83
+ <input v-if="showInput" ref="inputEl" />
84
+ </template>
85
+ ```
86
+
87
+ ```vue
88
+ <script setup>
89
+ import { useTemplateRef, watchEffect } from 'vue'
90
+
91
+ // Vue 3.5+ approach
92
+ const input = useTemplateRef('my-input')
93
+
94
+ // CORRECT: Use optional chaining for safe access
95
+ watchEffect(() => {
96
+ input.value?.focus()
97
+ })
98
+ </script>
99
+
100
+ <template>
101
+ <input v-if="showInput" ref="my-input" />
102
+ </template>
103
+ ```
104
+
105
+ ```vue
106
+ <script setup>
107
+ import { ref, onMounted } from 'vue'
108
+
109
+ const inputEl = ref(null)
110
+ const showInput = ref(true)
111
+
112
+ // ALTERNATIVE: Use v-show if you need consistent ref access
113
+ // v-show keeps element in DOM, just hides it with CSS
114
+ </script>
115
+
116
+ <template>
117
+ <!-- Element always exists in DOM, ref is never null -->
118
+ <input v-show="showInput" ref="inputEl" />
119
+ </template>
120
+ ```
121
+
122
+ ## Reference
123
+ - [Vue.js Template Refs](https://vuejs.org/guide/essentials/template-refs.html)
@@ -0,0 +1,104 @@
1
+ ---
2
+ title: Template Ref Unwrapping Only Works for Top-Level Properties
3
+ impact: MEDIUM
4
+ impactDescription: Nested refs in template expressions render as [object Object] instead of their values
5
+ type: capability
6
+ tags: [vue3, reactivity, ref, template, unwrapping]
7
+ ---
8
+
9
+ # Template Ref Unwrapping Only Works for Top-Level Properties
10
+
11
+ **Impact: MEDIUM** - Vue only auto-unwraps refs that are top-level properties in the template render context. Nested refs (refs inside objects) are NOT unwrapped in expressions, causing `[object Object]` rendering or calculation errors.
12
+
13
+ This caveat trips up developers when they store refs inside reactive objects or plain objects and try to use them in template expressions like `{{ object.count + 1 }}`.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Keep refs at the top level of your setup return or script setup
18
+ - [ ] Destructure nested refs to top-level variables before using in expressions
19
+ - [ ] Be aware that text interpolation `{{ object.ref }}` DOES unwrap, but expressions `{{ object.ref + 1 }}` do NOT
20
+ - [ ] Consider restructuring data to avoid nested refs in templates
21
+
22
+ **Incorrect:**
23
+ ```vue
24
+ <script setup>
25
+ import { ref } from 'vue'
26
+
27
+ const count = ref(0)
28
+ const object = { id: ref(1) }
29
+ </script>
30
+
31
+ <template>
32
+ <!-- WRONG: Nested ref in expression - does NOT unwrap -->
33
+ <p>ID + 1 = {{ object.id + 1 }}</p>
34
+ <!-- Renders: "ID + 1 = [object Object]1" -->
35
+
36
+ <!-- Surprisingly, plain interpolation DOES work -->
37
+ <p>ID = {{ object.id }}</p>
38
+ <!-- Renders: "ID = 1" (unwrapped because it's the final expression) -->
39
+ </template>
40
+ ```
41
+
42
+ **Correct:**
43
+ ```vue
44
+ <script setup>
45
+ import { ref } from 'vue'
46
+
47
+ const count = ref(0)
48
+ const object = { id: ref(1) }
49
+
50
+ // SOLUTION 1: Destructure to top-level
51
+ const { id } = object
52
+ </script>
53
+
54
+ <template>
55
+ <!-- CORRECT: Top-level ref unwraps in all expressions -->
56
+ <p>Count + 1 = {{ count + 1 }}</p>
57
+ <!-- Renders: "Count + 1 = 1" -->
58
+
59
+ <!-- CORRECT: Destructured ref is now top-level -->
60
+ <p>ID + 1 = {{ id + 1 }}</p>
61
+ <!-- Renders: "ID + 1 = 2" -->
62
+ </template>
63
+ ```
64
+
65
+ ```vue
66
+ <script setup>
67
+ import { ref, computed } from 'vue'
68
+
69
+ const object = { id: ref(1) }
70
+
71
+ // SOLUTION 2: Use computed for derived values
72
+ const idPlusOne = computed(() => object.id.value + 1)
73
+ </script>
74
+
75
+ <template>
76
+ <!-- CORRECT: Computed handles the .value access -->
77
+ <p>ID + 1 = {{ idPlusOne }}</p>
78
+ </template>
79
+ ```
80
+
81
+ ```vue
82
+ <script setup>
83
+ import { reactive } from 'vue'
84
+
85
+ // SOLUTION 3: Use reactive object instead (refs inside reactive auto-unwrap)
86
+ const object = reactive({ id: 1 })
87
+ </script>
88
+
89
+ <template>
90
+ <!-- CORRECT: Plain reactive property works in expressions -->
91
+ <p>ID + 1 = {{ object.id + 1 }}</p>
92
+ </template>
93
+ ```
94
+
95
+ ```javascript
96
+ // WHY this happens:
97
+ // - Template compilation only adds .value to top-level identifiers
98
+ // - {{ count + 1 }} compiles to: count.value + 1
99
+ // - {{ object.id + 1 }} compiles to: object.id + 1 (no .value added!)
100
+ // - Plain {{ object.id }} has special handling for display purposes
101
+ ```
102
+
103
+ ## Reference
104
+ - [Vue.js Reactivity Fundamentals - Caveat when Unwrapping in Templates](https://vuejs.org/guide/essentials/reactivity-fundamentals.html#caveat-when-unwrapping-in-templates)
@@ -0,0 +1,172 @@
1
+ ---
2
+ title: Template Ref Array Order Not Guaranteed in v-for
3
+ impact: MEDIUM
4
+ impactDescription: Refs collected from v-for may not match source array order, causing index-based bugs
5
+ type: gotcha
6
+ tags: [vue3, template-refs, v-for, arrays, ordering]
7
+ ---
8
+
9
+ # Template Ref Array Order Not Guaranteed in v-for
10
+
11
+ **Impact: MEDIUM** - When using template refs inside `v-for`, Vue collects the element references into an array. However, this array does NOT guarantee the same order as the source array. Relying on index-based access can lead to subtle bugs.
12
+
13
+ This caveat is not obvious and can cause hard-to-debug issues when you assume the ref array matches your data order.
14
+
15
+ > **Warning: `useTemplateRef()` does NOT work with v-for refs in Vue 3.5**
16
+ >
17
+ > The `useTemplateRef()` API returns `null` when used with refs inside `v-for`. This is a known limitation. You must use the legacy pattern with `ref()` and a matching template ref name:
18
+ >
19
+ > ```ts
20
+ > // Does NOT work with v-for - returns null
21
+ > const itemRefs = useTemplateRef('items')
22
+ >
23
+ > // Works with v-for - use this pattern instead
24
+ > const items = ref([]) // name must match ref="items" in template
25
+ > ```
26
+ >
27
+ > The examples in this rule show `useTemplateRef()` for illustration, but in practice you should use the legacy `ref()` pattern for v-for scenarios until this limitation is addressed.
28
+
29
+ ## Task Checklist
30
+
31
+ - [ ] Never assume ref array indices match source data array indices
32
+ - [ ] Use data attributes or other identifiers to correlate refs with data
33
+ - [ ] Consider function refs for complex scenarios requiring ordered access
34
+ - [ ] Test with dynamic list operations (add, remove, reorder) to verify behavior
35
+
36
+ **Incorrect:**
37
+ ```vue
38
+ <script setup>
39
+ import { ref, useTemplateRef, onMounted } from 'vue'
40
+
41
+ const items = ref(['First', 'Second', 'Third'])
42
+ const itemRefs = useTemplateRef('items')
43
+
44
+ onMounted(() => {
45
+ // WRONG: Assuming itemRefs[0] corresponds to items[0]
46
+ // The order is NOT guaranteed to match!
47
+ items.value.forEach((item, index) => {
48
+ console.log(`${item}: `, itemRefs.value[index]) // May be wrong element!
49
+ })
50
+ })
51
+ </script>
52
+
53
+ <template>
54
+ <ul>
55
+ <li v-for="item in items" ref="items" :key="item">
56
+ {{ item }}
57
+ </li>
58
+ </ul>
59
+ </template>
60
+ ```
61
+
62
+ **Correct:**
63
+ ```vue
64
+ <script setup>
65
+ import { ref, useTemplateRef, onMounted } from 'vue'
66
+
67
+ const items = ref([
68
+ { id: 1, text: 'First' },
69
+ { id: 2, text: 'Second' },
70
+ { id: 3, text: 'Third' }
71
+ ])
72
+ const itemRefs = useTemplateRef('items')
73
+
74
+ onMounted(() => {
75
+ // CORRECT: Use data attributes to identify elements
76
+ itemRefs.value.forEach(el => {
77
+ const id = el.dataset.id
78
+ const item = items.value.find(i => i.id === Number(id))
79
+ console.log(`${item.text}: `, el)
80
+ })
81
+ })
82
+ </script>
83
+
84
+ <template>
85
+ <ul>
86
+ <li
87
+ v-for="item in items"
88
+ ref="items"
89
+ :key="item.id"
90
+ :data-id="item.id"
91
+ >
92
+ {{ item.text }}
93
+ </li>
94
+ </ul>
95
+ </template>
96
+ ```
97
+
98
+ ```vue
99
+ <script setup>
100
+ import { ref, onMounted, onBeforeUpdate } from 'vue'
101
+
102
+ const items = ref(['First', 'Second', 'Third'])
103
+ const itemRefs = ref(new Map())
104
+
105
+ // CORRECT: Use function refs for precise control
106
+ function setItemRef(el, item) {
107
+ if (el) {
108
+ itemRefs.value.set(item, el)
109
+ } else {
110
+ itemRefs.value.delete(item)
111
+ }
112
+ }
113
+
114
+ // Reset before each update to handle removed items
115
+ onBeforeUpdate(() => {
116
+ itemRefs.value.clear()
117
+ })
118
+
119
+ onMounted(() => {
120
+ // Access refs by their associated data item
121
+ items.value.forEach(item => {
122
+ const el = itemRefs.value.get(item)
123
+ console.log(`${item}: `, el)
124
+ })
125
+ })
126
+ </script>
127
+
128
+ <template>
129
+ <ul>
130
+ <li
131
+ v-for="item in items"
132
+ :key="item"
133
+ :ref="(el) => setItemRef(el, item)"
134
+ >
135
+ {{ item }}
136
+ </li>
137
+ </ul>
138
+ </template>
139
+ ```
140
+
141
+ ```vue
142
+ <script setup>
143
+ import { ref, useTemplateRef, onMounted } from 'vue'
144
+
145
+ const items = ref(['First', 'Second', 'Third'])
146
+ const itemRefs = useTemplateRef('items')
147
+
148
+ // CORRECT: If order matters, sort refs by DOM position
149
+ onMounted(() => {
150
+ const sortedRefs = [...itemRefs.value].sort((a, b) => {
151
+ // Sort by DOM order using compareDocumentPosition
152
+ return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
153
+ })
154
+
155
+ // Now sortedRefs matches visual/DOM order
156
+ sortedRefs.forEach((el, index) => {
157
+ console.log(`Position ${index}: `, el.textContent)
158
+ })
159
+ })
160
+ </script>
161
+
162
+ <template>
163
+ <ul>
164
+ <li v-for="item in items" ref="items" :key="item">
165
+ {{ item }}
166
+ </li>
167
+ </ul>
168
+ </template>
169
+ ```
170
+
171
+ ## Reference
172
+ - [Vue.js Template Refs - Refs inside v-for](https://vuejs.org/guide/essentials/template-refs.html#refs-inside-v-for)
@@ -0,0 +1,72 @@
1
+ ---
2
+ title: Textarea Interpolation is One-Way Only - Use v-model for Two-Way Binding
3
+ impact: HIGH
4
+ impactDescription: Using {{ text }} inside textarea displays initial value but user input does NOT update the ref
5
+ type: capability
6
+ tags: [vue3, v-model, forms, textarea, interpolation, template]
7
+ ---
8
+
9
+ # Textarea Interpolation is One-Way Only - Use v-model for Two-Way Binding
10
+
11
+ **Impact: HIGH** - Interpolation in textarea (`{{ text }}`) provides one-way binding only - it displays the initial value but user input does NOT update the ref. This creates a confusing disconnect where the textarea shows content but edits are silently lost.
12
+
13
+ Unlike v-model which provides two-way binding, interpolation only renders the initial ref value into the textarea. When users type, the ref remains unchanged, making form submissions return stale data. Always use v-model for two-way binding in textareas.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Never use interpolation inside textarea tags
18
+ - [ ] Always use v-model for textarea two-way binding
19
+ - [ ] Search codebase for `<textarea>{{` patterns that may be silently broken
20
+ - [ ] Add linting rules to catch this pattern if possible
21
+
22
+ **Incorrect:**
23
+ ```html
24
+ <script setup>
25
+ import { ref } from 'vue'
26
+
27
+ const message = ref('Hello World')
28
+ </script>
29
+
30
+ <template>
31
+ <!-- WRONG: One-way binding only! Shows initial value but edits don't update ref -->
32
+ <textarea>{{ message }}</textarea>
33
+
34
+ <!-- Also WRONG: User can type but changes are lost -->
35
+ <textarea>{{ userBio }}</textarea>
36
+
37
+ <!-- The textarea displays content but ref never updates -->
38
+ </template>
39
+ ```
40
+
41
+ **Correct:**
42
+ ```html
43
+ <script setup>
44
+ import { ref } from 'vue'
45
+
46
+ const message = ref('Hello World')
47
+ </script>
48
+
49
+ <template>
50
+ <!-- CORRECT: Use v-model for textarea -->
51
+ <textarea v-model="message"></textarea>
52
+
53
+ <!-- For read-only display, still use v-model or :value -->
54
+ <textarea v-model="message" readonly></textarea>
55
+
56
+ <!-- Or one-way binding with :value -->
57
+ <textarea :value="message" readonly></textarea>
58
+ </template>
59
+ ```
60
+
61
+ ```html
62
+ <!-- With placeholder and other attributes -->
63
+ <textarea
64
+ v-model="message"
65
+ placeholder="Enter your message..."
66
+ rows="5"
67
+ maxlength="500"
68
+ ></textarea>
69
+ ```
70
+
71
+ ## Reference
72
+ - [Vue.js Form Input Bindings - Multiline text](https://vuejs.org/guide/essentials/forms.html#multiline-text)