@moon791017/neo-skills 1.0.28 → 1.0.29
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.
- package/GEMINI.md +2 -0
- package/README.md +5 -2
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/skills/neo-vue/SKILL.md +74 -0
- package/skills/neo-vue/reference/anti-patterns.md +180 -0
- package/skills/neo-vue/reference/coding-style.md +96 -0
- package/skills/neo-vue/reference/patterns.md +82 -0
package/GEMINI.md
CHANGED
|
@@ -109,3 +109,5 @@ When interacting with this codebase or using the extension, the agent follows th
|
|
|
109
109
|
* **SwiftUI 專家:** 支援 iOS 16.0+ 與 Swift 5.0+ 的現代開發模式,專注於 NavigationStack、Observation 框架、資料流架構及高效能視圖設計。
|
|
110
110
|
|
|
111
111
|
* **JavaScript 現代語法專家:** 跨版本 JavaScript 專家 (ES6-ES2025+),專注於現代化語法、模組系統與高效能開發模式。
|
|
112
|
+
|
|
113
|
+
* **Vue 現代開發專家:** 專注於 Vue 3 Composition API (`<script setup>`)、Pinia 狀態管理與 Vue Router 4,嚴格遵循官方 Style Guide 與最佳實踐。
|
package/README.md
CHANGED
|
@@ -56,10 +56,13 @@
|
|
|
56
56
|
### 8. JavaScript 開發專家
|
|
57
57
|
* **JavaScript 現代語法專家 (`skills/neo-javascript`)**:跨版本 JavaScript 專家 (ES6 - ES2025+),專注於現代化語法、模組系統與高效能開發模式。
|
|
58
58
|
|
|
59
|
-
### 9.
|
|
59
|
+
### 9. Vue 開發專家
|
|
60
|
+
* **Vue 3 現代開發專家 (`skills/neo-vue`)**:專注於 Vue 3 Composition API (`<script setup>`)、Pinia 狀態管理與 Vue Router 4。嚴格遵循官方 Style Guide 與最佳實踐,並提供反模式 (Anti-Patterns) 的避坑指引。
|
|
61
|
+
|
|
62
|
+
### 10. 需求釐清助手
|
|
60
63
|
* **需求釐清**:系統化引導用戶釐清模糊需求,並將其轉化為結構化的規格文件(背景、功能、約束、驗收標準)。
|
|
61
64
|
|
|
62
|
-
###
|
|
65
|
+
### 11. 安全守衛 (Security Guard)
|
|
63
66
|
* **主動防護 (`secret-guard.ts`)**:作為 CLI 的中介軟體 (Hook),自動攔截並掃描所有工具執行的參數。若偵測到敏感資訊(如環境設定檔、私鑰、雲端憑證等)將強制阻擋執行,防止機密外洩。
|
|
64
67
|
|
|
65
68
|
## 📂 系統架構
|
package/gemini-extension.json
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: neo-vue
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
category: "Core"
|
|
5
|
+
description: "Modern Vue 3 expert skill. Supports comprehensive applications from Composition API to Pinia and Vue Router, and strictly follows the official Vue Style Guide to ensure code quality and style consistency."
|
|
6
|
+
compatibility: "Supports Vue 3.x (Composition API), Pinia, and Vue Router 4. Adaptive to modern frontend build tools like Vite."
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Modern Vue 3 Expert Skill
|
|
10
|
+
|
|
11
|
+
## Trigger On
|
|
12
|
+
- The user asks to write, debug, refactor, or review Vue.js code.
|
|
13
|
+
- The project directory contains `*.vue`, `*.ts`/`*.js` files specifically importing Vue APIs, or Vue-related configuration files (e.g., `vite.config.ts`, `vue.config.js`).
|
|
14
|
+
- The user wants to implement state management using Pinia.
|
|
15
|
+
- The user wants to configure routing using Vue Router.
|
|
16
|
+
- The user asks for a code review on existing Vue code or architecture.
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
1. **Perceive (Environment Awareness):**
|
|
20
|
+
- Inspect project configuration (`package.json`, `vite.config.ts`) to identify Vue version (Vue 3 vs Vue 2) and tooling (Vite, TypeScript, ESLint, Prettier).
|
|
21
|
+
- Identify if the file is a Single-File Component (`.vue`), a composable (`useX.ts`), a store (`store.ts`), or router configuration.
|
|
22
|
+
- Detect the presence of Pinia or Vue Router in the project dependencies.
|
|
23
|
+
2. **Reason (Planning Phase):**
|
|
24
|
+
- Evaluate the modernization level of the current code. If Vue 2 Options API is used, plan for refactoring to Vue 3 Composition API with `<script setup>`.
|
|
25
|
+
- Mentally load the rules from `references/coding-style.md` and `references/anti-patterns.md` to ensure component naming (PascalCase), structure, and reactivity are correct.
|
|
26
|
+
- Mentally load the best practices from `references/patterns.md` to plan architecture, utilizing `ref` over `reactive`, and extracting logic into composables.
|
|
27
|
+
3. **Act (Execution Phase):**
|
|
28
|
+
- Write high-quality, idiomatic Vue 3 code using `<script setup>` syntax.
|
|
29
|
+
- Strictly follow component naming conventions (multi-word, `Base`/`The` prefixes) and structure (props validation, self-closing tags).
|
|
30
|
+
- Implement state using Pinia Setup Stores and handle routing with Named Routes.
|
|
31
|
+
4. **Validate (Standard Validation):**
|
|
32
|
+
- Validate that directive shorthands (`:`, `@`, `#`) are used consistently.
|
|
33
|
+
- Check that `computed` properties are pure and free of side effects.
|
|
34
|
+
- Ensure that props are not mutated directly.
|
|
35
|
+
- Verify that arrays iterated with `v-for` have stable and unique `key`s, and `v-if` is not used on the same element as `v-for`.
|
|
36
|
+
|
|
37
|
+
## Feature Roadmap (Vue 3 Core Features)
|
|
38
|
+
- **Composition API:** `<script setup>` for clean, boilerplate-free component authoring.
|
|
39
|
+
- **Reactivity:** `ref`, `reactive`, `computed`, `watch`, and `watchEffect` for explicit state management.
|
|
40
|
+
- **Macros:** `defineProps`, `defineEmits`, `defineExpose`, `defineOptions`, `defineModel` (Vue 3.4+) for compiler-aware declarations.
|
|
41
|
+
- **Composables:** Extracting and reusing stateful logic across components (e.g., `useFetch`, `useAuth`).
|
|
42
|
+
- **Teleport:** Render components outside the main DOM hierarchy (e.g., for modals or tooltips).
|
|
43
|
+
- **Suspense:** Handle asynchronous dependencies in the component tree gracefully.
|
|
44
|
+
- **Pinia:** State management using Setup Stores instead of Vuex.
|
|
45
|
+
- **Vue Router 4:** Modern routing utilizing Composition API hooks (`useRouter`, `useRoute`).
|
|
46
|
+
|
|
47
|
+
## Coding Standards
|
|
48
|
+
- **Component Naming:** Always use multi-word PascalCase for `.vue` files and component references in templates (e.g., `TodoItem.vue`, `<TodoItem />`).
|
|
49
|
+
- **Setup Script:** ALWAYS use `<script setup>` for new Vue components. Use TypeScript (`lang="ts"`) where possible.
|
|
50
|
+
- **Reactivity:** Prioritize `ref` over `reactive` for local state to maintain consistency in accessing values via `.value`.
|
|
51
|
+
- **Props and Emits:** Define Props and Emits strictly with type annotations using `defineProps<{ ... }>()` and `defineEmits<{ ... }>()`.
|
|
52
|
+
- **CSS Scoping:** Use `<style scoped>` or CSS Modules to prevent global style leakage.
|
|
53
|
+
|
|
54
|
+
## Deliver
|
|
55
|
+
- **Modern Architecture:** Provide Vue 3 Composition API-based solutions utilizing modern features and TypeScript.
|
|
56
|
+
- **Refactoring Insights:** Provide actionable suggestions to upgrade legacy Options API code to Composition API or Vuex to Pinia.
|
|
57
|
+
- **Style Compliance:** Ensure all delivered code adheres to the official Vue Style Guide (Priority A & B).
|
|
58
|
+
|
|
59
|
+
## Validate
|
|
60
|
+
- Ensure the provided code complies with Vue 3 syntax and best practices.
|
|
61
|
+
- Validate the avoidance of common anti-patterns (e.g., mutating props, side-effects in `computed`, missing `key` in `v-for`).
|
|
62
|
+
- Confirm the code is robust, readable, and properly utilizes Vue's reactivity system.
|
|
63
|
+
|
|
64
|
+
## Documentation
|
|
65
|
+
### Official References
|
|
66
|
+
- [Vue 3 Documentation](https://vuejs.org/guide/introduction.html)
|
|
67
|
+
- [Vue Style Guide](https://vuejs.org/style-guide/)
|
|
68
|
+
- [Pinia Documentation](https://pinia.vuejs.org/)
|
|
69
|
+
- [Vue Router Documentation](https://router.vuejs.org/)
|
|
70
|
+
|
|
71
|
+
### Internal References
|
|
72
|
+
- [Vue Coding Style and Naming Conventions Guide](reference/coding-style.md)
|
|
73
|
+
- [Vue Anti-Patterns and Best Practices](reference/anti-patterns.md)
|
|
74
|
+
- [Modern Vue Architecture Patterns Guide](reference/patterns.md)
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Vue 3 Anti-Patterns and Common Mistakes
|
|
2
|
+
|
|
3
|
+
This document lists common incorrect practices (Anti-Patterns) in Vue 3 (Composition API) development and provides the corresponding correct best practices to ensure code maintainability and performance.
|
|
4
|
+
|
|
5
|
+
## 1. Templates & Directives
|
|
6
|
+
|
|
7
|
+
### 1.1 Mixing `v-if` and `v-for` on the same element
|
|
8
|
+
**❌ Bad**:
|
|
9
|
+
When both exist on the same node, `v-if` has a higher priority than `v-for`. That means the `v-if` condition will not have access to variables from the scope of the `v-for`.
|
|
10
|
+
```vue-html
|
|
11
|
+
<!--
|
|
12
|
+
This will throw an error because property "todo"
|
|
13
|
+
is not defined on instance.
|
|
14
|
+
-->
|
|
15
|
+
<li v-for="todo in todos" v-if="!todo.isComplete">
|
|
16
|
+
{{ todo.name }}
|
|
17
|
+
</li>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**✅ Good**:
|
|
21
|
+
Move `v-for` to a wrapping `<template>` tag or replace the list with a computed property that returns the filtered list.
|
|
22
|
+
```vue-html
|
|
23
|
+
<template v-for="todo in todos">
|
|
24
|
+
<li v-if="!todo.isComplete">
|
|
25
|
+
{{ todo.name }}
|
|
26
|
+
</li>
|
|
27
|
+
</template>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 1.2 Using Array Index as `key` in `v-for`
|
|
31
|
+
**❌ Bad**:
|
|
32
|
+
Using the index can lead to severe rendering errors or mixed-up component states when the order of the array elements changes (e.g., additions, deletions, sorting).
|
|
33
|
+
```vue-html
|
|
34
|
+
<li v-for="(item, index) in items" :key="index">
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**✅ Good**:
|
|
38
|
+
Always use a unique and stable ID from your data.
|
|
39
|
+
```vue-html
|
|
40
|
+
<li v-for="item in items" :key="item.id">
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 2. Component Design & Data Flow
|
|
44
|
+
|
|
45
|
+
### 2.1 Mutating Props Directly
|
|
46
|
+
**❌ Bad**:
|
|
47
|
+
Props enforce a one-way data flow. Mutating them directly will cause Vue to emit warnings and break data synchronization between parent and child components.
|
|
48
|
+
```typescript
|
|
49
|
+
const props = defineProps<{ count: number }>();
|
|
50
|
+
|
|
51
|
+
function increment() {
|
|
52
|
+
// Error: Cannot mutate a prop directly
|
|
53
|
+
props.count++;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**✅ Good**:
|
|
58
|
+
Notify the parent component to update via `emit`, or use `v-model` (paired with `defineModel` in Vue 3.4+).
|
|
59
|
+
```typescript
|
|
60
|
+
const count = defineModel<number>('count');
|
|
61
|
+
|
|
62
|
+
function increment() {
|
|
63
|
+
// Correct: Two-way binding update via defineModel
|
|
64
|
+
count.value++;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2.2 Implicit Parent-Child Communication
|
|
69
|
+
**❌ Bad**:
|
|
70
|
+
Using `this.$parent` or modifying props directly to communicate state changes to the parent.
|
|
71
|
+
```javascript
|
|
72
|
+
function removeTodo() {
|
|
73
|
+
const parent = instance.parent
|
|
74
|
+
if (!parent) return
|
|
75
|
+
|
|
76
|
+
parent.props.todos = parent.props.todos.filter((todo) => {
|
|
77
|
+
return todo.id !== props.todo.id
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**✅ Good**:
|
|
83
|
+
Props down, events up. Emit an event to let the parent mutate the state.
|
|
84
|
+
```typescript
|
|
85
|
+
const emit = defineEmits(['delete']);
|
|
86
|
+
|
|
87
|
+
function removeTodo() {
|
|
88
|
+
emit('delete');
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 3. State Management & Reactivity
|
|
93
|
+
|
|
94
|
+
### 3.1 Side Effects in `computed`
|
|
95
|
+
**❌ Bad**:
|
|
96
|
+
Computed properties should be pure functions. You must never mutate other states, call APIs, or manipulate the DOM inside them.
|
|
97
|
+
```typescript
|
|
98
|
+
const sortedList = computed(() => {
|
|
99
|
+
// Error: Mutating external state
|
|
100
|
+
isSorting.value = true;
|
|
101
|
+
return list.value.slice().sort();
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**✅ Good**:
|
|
106
|
+
If you need to produce side effects, use `watch` or handle them inside event handler functions. `computed` is strictly for returning a new value based on dependencies.
|
|
107
|
+
```typescript
|
|
108
|
+
const sortedList = computed(() => {
|
|
109
|
+
return list.value.slice().sort();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
watch(sortedList, () => {
|
|
113
|
+
console.log('List was sorted!');
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 3.2 Abusing Provide / Inject for Global State
|
|
118
|
+
**❌ Bad**:
|
|
119
|
+
Stuffing large and frequently changing global states (like user info or configurations) into the root component's `provide`. This makes dependency tracking difficult and degrades performance.
|
|
120
|
+
|
|
121
|
+
**✅ Good**:
|
|
122
|
+
For global states that need to be shared across many different levels of components, use **Pinia**. Reserve `provide`/`inject` for developing specific UI component libraries or tightly scoped dependency injection.
|
|
123
|
+
|
|
124
|
+
## 4. DOM Manipulation & Styling
|
|
125
|
+
|
|
126
|
+
### 4.1 Direct DOM Manipulation (Bypassing Vue)
|
|
127
|
+
**❌ Bad**:
|
|
128
|
+
Using `document.querySelector` or manually manipulating DOM attributes breaks Vue's virtual DOM rendering mechanism.
|
|
129
|
+
```typescript
|
|
130
|
+
function focusInput() {
|
|
131
|
+
document.getElementById('my-input')?.focus();
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**✅ Good**:
|
|
136
|
+
Use Template Refs (`ref`) or `useTemplateRef` (Vue 3.5+) to obtain references to DOM elements.
|
|
137
|
+
```vue
|
|
138
|
+
<template>
|
|
139
|
+
<input ref="my-input" />
|
|
140
|
+
</template>
|
|
141
|
+
|
|
142
|
+
<script setup lang="ts">
|
|
143
|
+
import { useTemplateRef } from 'vue';
|
|
144
|
+
|
|
145
|
+
const inputRef = useTemplateRef('my-input');
|
|
146
|
+
|
|
147
|
+
function focusInput() {
|
|
148
|
+
inputRef.value?.focus();
|
|
149
|
+
}
|
|
150
|
+
</script>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 4.2 Element Selectors with `scoped`
|
|
154
|
+
**❌ Bad**:
|
|
155
|
+
Using element selectors (e.g., `button`, `p`) in `<style scoped>` can cause performance issues because Vue injects attributes (like `data-v-xxx`) to elements to scope them. Element-attribute selectors are much slower than class-attribute selectors.
|
|
156
|
+
```vue-html
|
|
157
|
+
<template>
|
|
158
|
+
<button>×</button>
|
|
159
|
+
</template>
|
|
160
|
+
|
|
161
|
+
<style scoped>
|
|
162
|
+
button {
|
|
163
|
+
background-color: red;
|
|
164
|
+
}
|
|
165
|
+
</style>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**✅ Good**:
|
|
169
|
+
Prefer class selectors in scoped styles.
|
|
170
|
+
```vue-html
|
|
171
|
+
<template>
|
|
172
|
+
<button class="btn-close">×</button>
|
|
173
|
+
</template>
|
|
174
|
+
|
|
175
|
+
<style scoped>
|
|
176
|
+
.btn-close {
|
|
177
|
+
background-color: red;
|
|
178
|
+
}
|
|
179
|
+
</style>
|
|
180
|
+
```
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Core Code Style Conventions for Vue 3
|
|
2
|
+
|
|
3
|
+
This document is extracted from the official Vue Style Guide, incorporating essential rules from Priority A (Essential) and Priority B (Strongly Recommended). When authoring Vue components and applications, it is imperative to adhere strictly to these conventions.
|
|
4
|
+
|
|
5
|
+
## 1. Naming Conventions
|
|
6
|
+
|
|
7
|
+
### 1.1 Component Naming
|
|
8
|
+
- **Multi-word component names**: Component names must consist of multiple words to prevent conflicts with future HTML tags (exceptions include the root `App` component and built-in components like `<transition>`).
|
|
9
|
+
- ✅ `TodoItem`, `UserProfile`
|
|
10
|
+
- ❌ `Todo`, `User`
|
|
11
|
+
- **Single-File Component (SFC) filename casing**: Filenames should consistently use **PascalCase** (preferred) or kebab-case.
|
|
12
|
+
- ✅ `MyComponent.vue`
|
|
13
|
+
- **Base component names**: Base, stateless, or purely presentational components should begin with a specific prefix such as `Base`, `App`, or `V`.
|
|
14
|
+
- ✅ `BaseButton.vue`, `BaseIcon.vue`, `AppTable.vue`
|
|
15
|
+
- **Single-instance component names**: Components that appear only once per page (e.g., navigation bars, sidebars) should use `The` as a prefix.
|
|
16
|
+
- ✅ `TheHeader.vue`, `TheSidebar.vue`
|
|
17
|
+
- **Tightly coupled component names**: If a child component is tightly coupled with its parent, the child's name should use the parent's name as a prefix.
|
|
18
|
+
- ✅ `TodoList.vue` -> `TodoListItem.vue` -> `TodoListItemButton.vue`
|
|
19
|
+
|
|
20
|
+
### 1.2 Component and Tag Syntax in Templates
|
|
21
|
+
- **Components in SFCs and string templates**: Always use **PascalCase** and ensure they are self-closing.
|
|
22
|
+
- ✅ `<MyComponent />`
|
|
23
|
+
- **Components in DOM templates**: Must use kebab-case.
|
|
24
|
+
- ✅ `<my-component></my-component>`
|
|
25
|
+
|
|
26
|
+
## 2. Properties and Data Flow (Props & Data Flow)
|
|
27
|
+
|
|
28
|
+
### 2.1 Prop Definitions
|
|
29
|
+
- **Detailed prop definitions**: The `type` must be explicitly specified. It is strongly recommended to provide `required` or `default` values, and include a `validator` when necessary.
|
|
30
|
+
```typescript
|
|
31
|
+
// ✅ Good
|
|
32
|
+
props: {
|
|
33
|
+
status: {
|
|
34
|
+
type: String,
|
|
35
|
+
required: true,
|
|
36
|
+
validator: (value: string) => ['syncing', 'synced', 'error'].includes(value)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 3. Template Syntax and Structure
|
|
42
|
+
|
|
43
|
+
### 3.1 Directive Shorthands
|
|
44
|
+
- Consistently use directive shorthands; do not mix full syntax with shorthands.
|
|
45
|
+
- `v-bind:` should be `:`
|
|
46
|
+
- `v-on:` should be `@`
|
|
47
|
+
- `v-slot:` should be `#`
|
|
48
|
+
|
|
49
|
+
### 3.2 Component Style Scoping
|
|
50
|
+
- Unless global styles are explicitly required, the `<style>` tag within an SFC should generally include the `scoped` attribute, or utilize CSS Modules (`<style module>`) to prevent global style pollution.
|
|
51
|
+
- Avoid using element selectors with `scoped` styling as it can impact performance. Prefer class selectors.
|
|
52
|
+
```vue
|
|
53
|
+
<!-- ✅ Good -->
|
|
54
|
+
<style scoped>
|
|
55
|
+
.btn-close {
|
|
56
|
+
background-color: red;
|
|
57
|
+
}
|
|
58
|
+
</style>
|
|
59
|
+
|
|
60
|
+
<!-- ❌ Bad -->
|
|
61
|
+
<style scoped>
|
|
62
|
+
button {
|
|
63
|
+
background-color: red;
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 4. Quoted Attribute Values
|
|
69
|
+
- **Non-empty HTML attribute values** should always be enclosed in quotes (single or double, choosing the one not used in the surrounding JavaScript).
|
|
70
|
+
```vue-html
|
|
71
|
+
<!-- ✅ Good -->
|
|
72
|
+
<input type="text">
|
|
73
|
+
<AppSidebar :style="{ width: sidebarWidth + 'px' }">
|
|
74
|
+
|
|
75
|
+
<!-- ❌ Bad -->
|
|
76
|
+
<input type=text>
|
|
77
|
+
<AppSidebar :style={width:sidebarWidth+'px'}>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## 5. Simple Expressions in Templates
|
|
81
|
+
- **Component templates should only contain simple expressions.** More complex logic should be refactored into computed properties or methods.
|
|
82
|
+
```vue-html
|
|
83
|
+
<!-- ✅ Good -->
|
|
84
|
+
{{ normalizedFullName }}
|
|
85
|
+
|
|
86
|
+
<!-- ❌ Bad -->
|
|
87
|
+
{{
|
|
88
|
+
fullName.split(' ').map((word) => {
|
|
89
|
+
return word[0].toUpperCase() + word.slice(1)
|
|
90
|
+
}).join(' ')
|
|
91
|
+
}}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 6. Order of Options and Attributes
|
|
95
|
+
- **Component/instance options should be ordered consistently.** (e.g., `name` -> `components` -> `props` -> `emits` -> `setup` -> `computed` -> `watch` -> lifecycle hooks -> `methods` -> `template`).
|
|
96
|
+
- **Attributes of elements should be ordered consistently.** (e.g., `is` -> `v-for` -> `v-if`/`v-show` -> `id` -> `ref`/`key` -> `v-model` -> other attributes -> `v-on` -> `v-html`/`v-text`).
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Vue 3 Architecture and Modern Patterns
|
|
2
|
+
|
|
3
|
+
This document defines the architectural guidelines for modern Vue 3 development, covering Composition API practices, Pinia state management principles, Vue Router configuration recommendations, and general best practices for building robust Vue applications.
|
|
4
|
+
|
|
5
|
+
## 1. Composition API and Script Setup
|
|
6
|
+
|
|
7
|
+
- **Prefer `<script setup>`**: All new Single-File Components (SFCs) must use the `<script setup>` syntax to achieve concise code, better runtime performance, and superior TypeScript support.
|
|
8
|
+
- **Reactivity**:
|
|
9
|
+
- Prefer using `ref()` for defining both primitive types and objects to maintain consistency in how values are accessed (always via `.value`).
|
|
10
|
+
- Only use `reactive()` when there is an explicit need to flatten an object's properties for reactivity without `.value`, or when integrating with external state systems that require it.
|
|
11
|
+
- When returning state from composables, prefer returning an object of refs over a single reactive object to allow consumers to destructure without losing reactivity (or use `toRefs()`).
|
|
12
|
+
- **Props and Emits**:
|
|
13
|
+
- Use `defineProps()` and `defineEmits()` to define component interfaces.
|
|
14
|
+
- In TypeScript, it is strongly recommended to define them using pure type declarations:
|
|
15
|
+
```typescript
|
|
16
|
+
const props = defineProps<{
|
|
17
|
+
id: number;
|
|
18
|
+
title?: string;
|
|
19
|
+
}>();
|
|
20
|
+
const emit = defineEmits<{
|
|
21
|
+
(e: 'update', id: number): void;
|
|
22
|
+
}>();
|
|
23
|
+
```
|
|
24
|
+
- For default prop values with type-based declarations, utilize Reactive Props Destructure (Vue 3.5+) or `withDefaults()`.
|
|
25
|
+
- **Computed Properties (`computed`)**:
|
|
26
|
+
- `computed` must be a pure function. Never generate side effects or mutate the original state inside a `computed` getter.
|
|
27
|
+
- Avoid mutating the value returned by a computed property; treat it as read-only derived state.
|
|
28
|
+
- Break down complex computed properties into multiple simpler computed properties for better readability and testability.
|
|
29
|
+
- **Logic Reuse (Composables)**:
|
|
30
|
+
- Encapsulate complex business logic, state, and side effects into Composables (e.g., `useAuth()`, `useUser()`).
|
|
31
|
+
- Composables should follow the naming convention of starting with `use`.
|
|
32
|
+
- The return values of Composables should be destructured into `ref`s for external use.
|
|
33
|
+
- Properly clean up side effects (like event listeners or timers) inside `onUnmounted()` within the composable.
|
|
34
|
+
- Use `toValue()` (Vue 3.3+) to normalize arguments that can be either a value, a ref, or a getter.
|
|
35
|
+
|
|
36
|
+
## 2. State Management (Pinia)
|
|
37
|
+
|
|
38
|
+
- **Use Setup Stores**: When defining Pinia stores, use the Setup syntax (similar to Composition API) rather than the Options syntax. This provides better alignment with the Composition API mental model and allows the use of composables within stores.
|
|
39
|
+
```typescript
|
|
40
|
+
export const useUserStore = defineStore('user', () => {
|
|
41
|
+
const name = ref('Alice');
|
|
42
|
+
const isAuthenticated = computed(() => !!name.value);
|
|
43
|
+
function login(newName: string) {
|
|
44
|
+
name.value = newName;
|
|
45
|
+
}
|
|
46
|
+
return { name, isAuthenticated, login };
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
- **Separation of Concerns**: Stores should focus solely on maintaining global/shared state. Trivial logic related to UI interactions or component-local state should remain within the Components.
|
|
50
|
+
|
|
51
|
+
## 3. Routing Configuration (Vue Router)
|
|
52
|
+
|
|
53
|
+
- **Use Named Routes**:
|
|
54
|
+
- During development, always prefer navigating using the route `name` rather than the `path`. This prevents broken links if URL paths are refactored in the future.
|
|
55
|
+
```typescript
|
|
56
|
+
// ✅ Good
|
|
57
|
+
router.push({ name: 'UserProfile', params: { id: 123 } });
|
|
58
|
+
|
|
59
|
+
// ❌ Bad
|
|
60
|
+
router.push(`/user/${id}`);
|
|
61
|
+
```
|
|
62
|
+
- **Route Guards**:
|
|
63
|
+
- Place permission validation, authentication checks, and global interception logic in global `beforeEach` guards or route-specific guards, rather than handling them inside a component's `onMounted` lifecycle hook.
|
|
64
|
+
|
|
65
|
+
## 4. Component Communication
|
|
66
|
+
|
|
67
|
+
- **Props Down, Events Up**: Adhere to the one-way data flow principle. Parents pass data down via props, and children communicate changes back up by emitting events.
|
|
68
|
+
- **Avoid Implicit Communication**: Do not use `this.$parent` or attempt to directly mutate props. If a child needs to update a prop value, it must emit an event to request the parent to perform the mutation.
|
|
69
|
+
- **Two-Way Binding (`v-model`)**: Use `defineModel()` (Vue 3.4+) to easily create two-way bindings for components.
|
|
70
|
+
|
|
71
|
+
## 5. Performance Optimizations
|
|
72
|
+
|
|
73
|
+
- **Virtualize Large Lists**: For rendering massive lists, use list virtualization libraries (like `vue-virtual-scroller`) instead of rendering all DOM nodes upfront.
|
|
74
|
+
- **Stable Props**: Try to keep props passed to child components as stable as possible to prevent unnecessary re-renders.
|
|
75
|
+
- **`v-once` and `v-memo`**: Utilize `v-once` for static content that never updates, and `v-memo` (Vue 3.2+) for conditionally skipping updates in large sub-trees or `v-for` lists based on dependency arrays.
|
|
76
|
+
- **Shallow Reactivity**: Use `shallowRef()` and `shallowReactive()` when dealing with large immutable data structures where deep reactivity tracking overhead is undesirable.
|
|
77
|
+
|
|
78
|
+
## 6. Provide / Inject
|
|
79
|
+
|
|
80
|
+
- Use `provide` and `inject` to avoid "prop drilling" when passing data through deeply nested component trees.
|
|
81
|
+
- Keep mutations to reactive state inside the provider whenever possible. If an injector needs to update the data, the provider should also provide a function responsible for the mutation.
|
|
82
|
+
- Use `Symbol` keys for injection to avoid naming collisions in large applications.
|