Implementing Composable Architecture in Vue.js

Techniques for building modular and reusable components in Vue.js to enhance code maintainability and scalability.

0 likes
9 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(); }); });"
        }
      ]
    }
  ]
}