Implementing Composable Architecture in Vue.js
Techniques for building modular and reusable components in Vue.js to enhance code maintainability and scalability.
0 likes
135 views
Rule Content
{
"title": "Implementing Composable Architecture in Vue.js",
"description": "Techniques for building modular and reusable components in Vue.js to enhance code maintainability and scalability.",
"category": "Vue Cursor Rules",
"rules": [
{
"name": "Organize Composables by Feature or Domain",
"description": "Group composables based on their purpose or the feature they support to keep the project organized and facilitate code management.",
"pattern": "src/composables/{feature}/use{Feature}.js",
"examples": [
{
"before": "src/composables/useAuth.js",
"after": "src/composables/auth/useAuth.js"
},
{
"before": "src/composables/useFetch.js",
"after": "src/composables/data/useFetch.js"
}
]
},
{
"name": "Follow Consistent Naming Conventions",
"description": "Prefix composables with 'use' and use descriptive names to clearly indicate their functionality.",
"pattern": "use[A-Z][a-zA-Z]+",
"examples": [
{
"before": "fetchData.js",
"after": "useFetchData.js"
},
{
"before": "auth.js",
"after": "useAuth.js"
}
]
},
{
"name": "Encapsulate State and Logic Together",
"description": "Composables should encapsulate related state and logic, returning both in an object to promote modularity and reusability.",
"pattern": "export function use[A-Z][a-zA-Z]+\\(\\) {.*return {.*};.*}",
"examples": [
{
"before": "export function useCounter() { const count = ref(0); const increment = () => count.value++; return increment; }",
"after": "export function useCounter() { const count = ref(0); const increment = () => count.value++; return { count, increment }; }"
}
]
},
{
"name": "Keep Composables Focused and Single-Responsibility",
"description": "Ensure each composable addresses a single concern to simplify understanding, testing, and maintenance.",
"pattern": "export function use[A-Z][a-zA-Z]+\\(\\) {.*}",
"examples": [
{
"before": "export function useAuthAndFetch() { /* authentication and data fetching logic */ }",
"after": "export function useAuth() { /* authentication logic */ } export function useFetch() { /* data fetching logic */ }"
}
]
},
{
"name": "Use TypeScript or JSDoc for Better Intellisense",
"description": "Define types or use JSDoc annotations in composables to enhance code clarity and reduce bugs.",
"pattern": "/\\*\\*.*@returns {.*}.*\\*/\\nexport function use[A-Z][a-zA-Z]+\\(\\) {.*}",
"examples": [
{
"before": "export function useAuth() { /* authentication logic */ }",
"after": "/** * Manages user authentication state. * @returns {Object} - Authentication state and methods */ export function useAuth() { /* authentication logic */ }"
}
]
},
{
"name": "Document Composables Thoroughly",
"description": "Add comments explaining the purpose, parameters, and return values of composables to facilitate understanding and usage.",
"pattern": "/\\*\\*.*\\*/\\nexport function use[A-Z][a-zA-Z]+\\(\\) {.*}",
"examples": [
{
"before": "export function useFetchData() { /* data fetching logic */ }",
"after": "/** * Fetches data from the API. * @returns {Object} - Data and error state */ export function useFetchData() { /* data fetching logic */ }"
}
]
},
{
"name": "Use Watchers and WatchEffects Carefully",
"description": "Manage side effects and cleanup properly when using 'watch' or 'watchEffect' in composables to avoid memory leaks.",
"pattern": "watch\\(.*\\) {.*onBeforeUnmount\\(.*\\);.*}",
"examples": [
{
"before": "watch(data, save);",
"after": "const stopWatch = watch(data, save); onBeforeUnmount(() => { stopWatch(); });"
}
]
},
{
"name": "Isolate API Calls in Composables",
"description": "Keep API calls within their own composables to separate data-fetching logic from presentation components.",
"pattern": "export function use[A-Z][a-zA-Z]+\\(\\) {.*axios\\.get\\(.*\\).*}",
"examples": [
{
"before": "export function useComponentLogic() { /* component logic */ axios.get('/api/data'); }",
"after": "export function useFetchData() { /* data fetching logic */ axios.get('/api/data'); }"
}
]
},
{
"name": "Avoid Overuse of Reactive Variables",
"description": "Use reactive state only when necessary to minimize performance overhead and complexity.",
"pattern": "const [a-zA-Z]+ = ref\\(.*\\);",
"examples": [
{
"before": "const isActive = ref(false);",
"after": "let isActive = false;"
}
]
},
{
"name": "Test Composables Independently",
"description": "Write unit tests for composables to ensure their functionality before integrating them into components.",
"pattern": "describe\\('use[A-Z][a-zA-Z]+', () => {.*it\\('.*', () => {.*expect\\(.*\\).toBe\\(.*\\);.*}.*});",
"examples": [
{
"before": "// No tests",
"after": "describe('useFetchData', () => { it('fetches data successfully', () => { const { data } = useFetchData(); expect(data.value).toBeDefined(); }); });"
}
]
}
]
}