11 KiB
11 KiB
| 1 | No | Category | Guideline | Description | Do | Don't | Code Good | Code Bad | Severity | Docs URL |
|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 1 | Composition | Use Composition API for new projects | Composition API offers better TypeScript support and logic reuse | <script setup> for components | Options API for new projects | <script setup> | export default { data() } | Medium | https://vuejs.org/guide/extras/composition-api-faq.html |
| 3 | 2 | Composition | Use script setup syntax | Cleaner syntax with automatic exports | <script setup> with defineProps | setup() function manually | <script setup> | <script> setup() { return {} } | Low | https://vuejs.org/api/sfc-script-setup.html |
| 4 | 3 | Reactivity | Use ref for primitives | ref() for primitive values that need reactivity | ref() for strings numbers booleans | reactive() for primitives | const count = ref(0) | const count = reactive(0) | Medium | https://vuejs.org/guide/essentials/reactivity-fundamentals.html |
| 5 | 4 | Reactivity | Use reactive for objects | reactive() for complex objects and arrays | reactive() for objects with multiple properties | ref() for complex objects | const state = reactive({ user: null }) | const state = ref({ user: null }) | Medium | |
| 6 | 5 | Reactivity | Access ref values with .value | Remember .value in script unwrap in template | Use .value in script | Forget .value in script | count.value++ | count++ (in script) | High | |
| 7 | 6 | Reactivity | Use computed for derived state | Computed properties cache and update automatically | computed() for derived values | Methods for derived values | const doubled = computed(() => count.value * 2) | const doubled = () => count.value * 2 | Medium | https://vuejs.org/guide/essentials/computed.html |
| 8 | 7 | Reactivity | Use shallowRef for large objects | Avoid deep reactivity for performance | shallowRef for large data structures | ref for large nested objects | const bigData = shallowRef(largeObject) | const bigData = ref(largeObject) | Medium | https://vuejs.org/api/reactivity-advanced.html#shallowref |
| 9 | 8 | Watchers | Use watchEffect for simple cases | Auto-tracks dependencies | watchEffect for simple reactive effects | watch with explicit deps when not needed | watchEffect(() => console.log(count.value)) | watch(count, (val) => console.log(val)) | Low | https://vuejs.org/guide/essentials/watchers.html |
| 10 | 9 | Watchers | Use watch for specific sources | Explicit control over what to watch | watch with specific refs | watchEffect for complex conditional logic | watch(userId, fetchUser) | watchEffect with conditionals | Medium | |
| 11 | 10 | Watchers | Clean up side effects | Return cleanup function in watchers | Return cleanup in watchEffect | Leave subscriptions open | watchEffect((onCleanup) => { onCleanup(unsub) }) | watchEffect without cleanup | High | |
| 12 | 11 | Props | Define props with defineProps | Type-safe prop definitions | defineProps with TypeScript | Props without types | defineProps<{ msg: string }>() | defineProps(['msg']) | Medium | https://vuejs.org/guide/typescript/composition-api.html#typing-component-props |
| 13 | 12 | Props | Use withDefaults for default values | Provide defaults for optional props | withDefaults with defineProps | Defaults in destructuring | withDefaults(defineProps<Props>(), { count: 0 }) | const { count = 0 } = defineProps() | Medium | |
| 14 | 13 | Props | Avoid mutating props | Props should be read-only | Emit events to parent for changes | Direct prop mutation | emit('update:modelValue', newVal) | props.modelValue = newVal | High | |
| 15 | 14 | Emits | Define emits with defineEmits | Type-safe event emissions | defineEmits with types | Emit without definition | defineEmits<{ change: [id: number] }>() | emit('change', id) without define | Medium | https://vuejs.org/guide/typescript/composition-api.html#typing-component-emits |
| 16 | 15 | Emits | Use v-model for two-way binding | Simplified parent-child data flow | v-model with modelValue prop | :value + @input manually | <Child v-model="value"/> | <Child :value="value" @input="value = $event"/> | Low | https://vuejs.org/guide/components/v-model.html |
| 17 | 16 | Lifecycle | Use onMounted for DOM access | DOM is ready in onMounted | onMounted for DOM operations | Access DOM in setup directly | onMounted(() => el.value.focus()) | el.value.focus() in setup | High | https://vuejs.org/api/composition-api-lifecycle.html |
| 18 | 17 | Lifecycle | Clean up in onUnmounted | Remove listeners and subscriptions | onUnmounted for cleanup | Leave listeners attached | onUnmounted(() => window.removeEventListener()) | No cleanup on unmount | High | |
| 19 | 18 | Lifecycle | Avoid onBeforeMount for data | Use onMounted or setup for data fetching | Fetch in onMounted or setup | Fetch in onBeforeMount | onMounted(async () => await fetchData()) | onBeforeMount(async () => await fetchData()) | Low | |
| 20 | 19 | Components | Use single-file components | Keep template script style together | .vue files for components | Separate template/script files | Component.vue with all parts | Component.js + Component.html | Low | |
| 21 | 20 | Components | Use PascalCase for components | Consistent component naming | PascalCase in imports and templates | kebab-case in script | <MyComponent/> | <my-component/> | Low | https://vuejs.org/style-guide/rules-strongly-recommended.html |
| 22 | 21 | Components | Prefer composition over mixins | Composables replace mixins | Composables for shared logic | Mixins for code reuse | const { data } = useApi() | mixins: [apiMixin] | Medium | |
| 23 | 22 | Composables | Name composables with use prefix | Convention for composable functions | useFetch useAuth useForm | getData or fetchApi | export function useFetch() | export function fetchData() | Medium | https://vuejs.org/guide/reusability/composables.html |
| 24 | 23 | Composables | Return refs from composables | Maintain reactivity when destructuring | Return ref values | Return reactive objects that lose reactivity | return { data: ref(null) } | return reactive({ data: null }) | Medium | |
| 25 | 24 | Composables | Accept ref or value params | Use toValue for flexible inputs | toValue() or unref() for params | Only accept ref or only value | const val = toValue(maybeRef) | const val = maybeRef.value | Low | https://vuejs.org/api/reactivity-utilities.html#tovalue |
| 26 | 25 | Templates | Use v-bind shorthand | Cleaner template syntax | :prop instead of v-bind:prop | Full v-bind syntax | <div :class="cls"> | <div v-bind:class="cls"> | Low | |
| 27 | 26 | Templates | Use v-on shorthand | Cleaner event binding | @event instead of v-on:event | Full v-on syntax | <button @click="handler"> | <button v-on:click="handler"> | Low | |
| 28 | 27 | Templates | Avoid v-if with v-for | v-if has higher priority causes issues | Wrap in template or computed filter | v-if on same element as v-for | <template v-for><div v-if> | <div v-for v-if> | High | https://vuejs.org/style-guide/rules-essential.html#avoid-v-if-with-v-for |
| 29 | 28 | Templates | Use key with v-for | Proper list rendering and updates | Unique key for each item | Index as key for dynamic lists | v-for="item in items" :key="item.id" | v-for="(item, i) in items" :key="i" | High | |
| 30 | 29 | State | Use Pinia for global state | Official state management for Vue 3 | Pinia stores for shared state | Vuex for new projects | const store = useCounterStore() | Vuex with mutations | Medium | https://pinia.vuejs.org/ |
| 31 | 30 | State | Define stores with defineStore | Composition API style stores | Setup stores with defineStore | Options stores for complex state | defineStore('counter', () => {}) | defineStore('counter', { state }) | Low | |
| 32 | 31 | State | Use storeToRefs for destructuring | Maintain reactivity when destructuring | storeToRefs(store) | Direct destructuring | const { count } = storeToRefs(store) | const { count } = store | High | https://pinia.vuejs.org/core-concepts/#destructuring-from-a-store |
| 33 | 32 | Routing | Use useRouter and useRoute | Composition API router access | useRouter() useRoute() in setup | this.$router this.$route | const router = useRouter() | this.$router.push() | Medium | https://router.vuejs.org/guide/advanced/composition-api.html |
| 34 | 33 | Routing | Lazy load route components | Code splitting for routes | () => import() for components | Static imports for all routes | component: () => import('./Page.vue') | component: Page | Medium | https://router.vuejs.org/guide/advanced/lazy-loading.html |
| 35 | 34 | Routing | Use navigation guards | Protect routes and handle redirects | beforeEach for auth checks | Check auth in each component | router.beforeEach((to) => {}) | Check auth in onMounted | Medium | |
| 36 | 35 | Performance | Use v-once for static content | Skip re-renders for static elements | v-once on never-changing content | v-once on dynamic content | <div v-once>{{ staticText }}</div> | <div v-once>{{ dynamicText }}</div> | Low | https://vuejs.org/api/built-in-directives.html#v-once |
| 37 | 36 | Performance | Use v-memo for expensive lists | Memoize list items | v-memo with dependency array | Re-render entire list always | <div v-for v-memo="[item.id]"> | <div v-for> without memo | Medium | https://vuejs.org/api/built-in-directives.html#v-memo |
| 38 | 37 | Performance | Use shallowReactive for flat objects | Avoid deep reactivity overhead | shallowReactive for flat state | reactive for simple objects | shallowReactive({ count: 0 }) | reactive({ count: 0 }) | Low | |
| 39 | 38 | Performance | Use defineAsyncComponent | Lazy load heavy components | defineAsyncComponent for modals dialogs | Import all components eagerly | defineAsyncComponent(() => import()) | import HeavyComponent from | Medium | https://vuejs.org/guide/components/async.html |
| 40 | 39 | TypeScript | Use generic components | Type-safe reusable components | Generic with defineComponent | Any types in components | <script setup lang="ts" generic="T"> | <script setup> without types | Medium | https://vuejs.org/guide/typescript/composition-api.html |
| 41 | 40 | TypeScript | Type template refs | Proper typing for DOM refs | ref<HTMLInputElement>(null) | ref(null) without type | const input = ref<HTMLInputElement>(null) | const input = ref(null) | Medium | |
| 42 | 41 | TypeScript | Use PropType for complex props | Type complex prop types | PropType<User> for object props | Object without type | type: Object as PropType<User> | type: Object | Medium | |
| 43 | 42 | Testing | Use Vue Test Utils | Official testing library | mount shallowMount for components | Manual DOM testing | import { mount } from '@vue/test-utils' | document.createElement | Medium | https://test-utils.vuejs.org/ |
| 44 | 43 | Testing | Test component behavior | Focus on inputs and outputs | Test props emit and rendered output | Test internal implementation | expect(wrapper.text()).toContain() | expect(wrapper.vm.internalState) | Medium | |
| 45 | 44 | Forms | Use v-model modifiers | Built-in input handling | .lazy .number .trim modifiers | Manual input parsing | <input v-model.number="age"> | <input v-model="age"> then parse | Low | https://vuejs.org/guide/essentials/forms.html#modifiers |
| 46 | 45 | Forms | Use VeeValidate or FormKit | Form validation libraries | VeeValidate for complex forms | Manual validation logic | useField useForm from vee-validate | Custom validation in each input | Medium | |
| 47 | 46 | Accessibility | Use semantic elements | Proper HTML elements in templates | button nav main for purpose | div for everything | <button @click> | <div @click> | High | |
| 48 | 47 | Accessibility | Bind aria attributes dynamically | Keep ARIA in sync with state | :aria-expanded="isOpen" | Static ARIA values | :aria-expanded="menuOpen" | aria-expanded="true" | Medium | |
| 49 | 48 | SSR | Use Nuxt for SSR | Full-featured SSR framework | Nuxt 3 for SSR apps | Manual SSR setup | npx nuxi init my-app | Custom SSR configuration | Medium | https://nuxt.com/ |
| 50 | 49 | SSR | Handle hydration mismatches | Client/server content must match | ClientOnly for browser-only content | Different content server/client | <ClientOnly><BrowserWidget/></ClientOnly> | <div>{{ Date.now() }}</div> | High |