Bird
Raised Fist0
Vueframework~8 mins

Creating a custom composable in Vue - Performance Optimization Steps

Choose your learning style10 modes available
Performance: Creating a custom composable
MEDIUM IMPACT
This affects how efficiently reactive logic is reused and how many reactive dependencies are tracked, impacting interaction responsiveness and memory usage.
Reusing reactive logic across multiple components
Vue
import { ref } from 'vue';

const sharedCount = ref(0);

export function useSharedCounter() {
  function increment() {
    sharedCount.value++;
  }
  return { count: sharedCount, increment };
}

// Components share the same reactive state
Sharing reactive state reduces duplicated reactive tracking and memory usage, improving responsiveness.
📈 Performance GainReactive dependencies tracked once, improving INP and reducing memory footprint.
Reusing reactive logic across multiple components
Vue
import { ref } from 'vue';

export function useCounter() {
  const count = ref(0);
  function increment() {
    count.value++;
  }
  return { count, increment };
}

// Used inside multiple components without memoization or shared state
Each component creates its own reactive state, causing duplicated reactive tracking and more memory use.
📉 Performance CostIncreases reactive dependencies linearly with component instances, causing slower INP and higher memory.
Performance Comparison
PatternReactive DependenciesMemory UsageUpdate CostVerdict
Separate reactive state per componentHigh (one per instance)HighHigh (many updates)[X] Bad
Shared reactive state in composableLow (shared once)LowLow (single source)[OK] Good
Rendering Pipeline
Custom composables create reactive dependencies that Vue tracks. When reactive state changes, Vue schedules component updates affecting the rendering pipeline stages.
→Reactive Dependency Tracking
→Component Update
→Render
→Paint
⚠️ BottleneckReactive Dependency Tracking and Component Update stages can become expensive if many duplicated reactive states exist.
Core Web Vital Affected
INP
This affects how efficiently reactive logic is reused and how many reactive dependencies are tracked, impacting interaction responsiveness and memory usage.
Optimization Tips
1Avoid creating new reactive state inside composables for each component if shared state suffices.
2Reuse reactive dependencies to reduce memory and update overhead.
3Profile reactive updates to detect duplicated reactive tracking.
Performance Quiz - 3 Questions
Test your performance knowledge
What is a performance risk when creating a custom composable that returns new reactive state for each component instance?
AIt causes layout shifts (CLS)
BDuplicated reactive tracking increases memory and update cost
CIt blocks the initial page load
DIt reduces bundle size
DevTools: Performance
How to check: Record a performance profile while interacting with components using the composable. Look for many reactive updates or long scripting times.
What to look for: High scripting time or many reactive updates indicate duplicated reactive state; fewer updates and shorter scripting time indicate efficient composables.

Practice

(1/5)
1. What is the main purpose of creating a custom composable in Vue?
easy
A. To define global CSS styles
B. To reuse reactive logic across multiple components
C. To create new Vue directives
D. To register components globally

Solution

  1. Step 1: Understand what composables do

    Custom composables are functions that encapsulate reactive logic to be reused.
  2. Step 2: Identify the purpose in Vue components

    They help share logic without repeating code in multiple components.
  3. Final Answer:

    To reuse reactive logic across multiple components -> Option B
  4. Quick Check:

    Reusable reactive logic = B [OK]
Hint: Composables = reusable reactive logic functions [OK]
Common Mistakes:
  • Confusing composables with styling or directives
  • Thinking composables register components
  • Assuming composables handle templates
2. Which of the following is the correct way to start a custom composable function in Vue 3?
easy
A. function useData() { const count = ref(0); return { count }; }
B. const useData = () => { const count = reactive(0); return count; }
C. function useData() { const count = ref(0); }
D. const useData = () => { return ref({ count: 0 }); }

Solution

  1. Step 1: Check function declaration and return

    function useData() { const count = ref(0); return { count }; } declares a function named with 'use' prefix and returns reactive refs.
  2. Step 2: Validate reactive usage and return object

    function useData() { const count = ref(0); return { count }; } returns an object with the reactive ref, which is correct pattern.
  3. Final Answer:

    function useData() { const count = ref(0); return { count }; } -> Option A
  4. Quick Check:

    Composable returns reactive refs in object = D [OK]
Hint: Composable must return reactive data inside an object [OK]
Common Mistakes:
  • Not returning reactive data
  • Returning reactive primitive directly instead of object
  • Missing 'use' prefix in function name
3. Given this composable code, what will be logged when calling increment() twice?
import { ref } from 'vue';

export function useCounter() {
  const count = ref(0);
  function increment() {
    count.value++;
    console.log(count.value);
  }
  return { count, increment };
}
medium
A. Undefined and then 1
B. 0 and then 1
C. 2 and then 3
D. 1 and then 2

Solution

  1. Step 1: Understand initial count value

    The count starts at 0 as a ref.
  2. Step 2: Trace increment calls and console logs

    Each increment increases count.value by 1 and logs it immediately.
  3. Final Answer:

    1 and then 2 -> Option D
  4. Quick Check:

    Increment twice logs 1 then 2 [OK]
Hint: Ref value increments and logs immediately [OK]
Common Mistakes:
  • Confusing ref.value with ref itself
  • Assuming count starts at 1
  • Thinking console logs before increment
4. Identify the error in this composable code:
import { ref } from 'vue';

export function useToggle() {
  const state = ref(false);
  function toggle() {
    state = !state;
  }
  return { state, toggle };
}
medium
A. ref should be reactive instead
B. Missing return statement
C. Cannot reassign a const ref variable directly
D. toggle function should be async

Solution

  1. Step 1: Check how ref is updated

    The code tries to assign 'state = !state', which reassigns the const variable.
  2. Step 2: Correct way to update ref value

    Should update 'state.value = !state.value' instead of reassigning 'state'.
  3. Final Answer:

    Cannot reassign a const ref variable directly -> Option C
  4. Quick Check:

    Update ref.value, not ref variable [OK]
Hint: Update ref.value, never reassign ref variable [OK]
Common Mistakes:
  • Reassigning ref variable instead of its value
  • Forgetting to use .value to access ref
  • Assuming reactive is needed instead of ref
5. You want to create a composable that fetches user data and exposes loading state. Which approach correctly combines reactive state and async fetch inside a composable?
hard
A. function useUser() { const user = ref(null); const loading = ref(true); async function fetchUser() { loading.value = true; user.value = await fetch('/api/user').then(r => r.json()); loading.value = false; } fetchUser(); return { user, loading }; }
B. function useUser() { const user = reactive(null); const loading = false; async function fetchUser() { loading = true; user = await fetch('/api/user').then(r => r.json()); loading = false; } fetchUser(); return { user, loading }; }
C. function useUser() { const user = ref(null); const loading = ref(false); function fetchUser() { loading.value = true; fetch('/api/user').then(r => r.json()).then(data => { user.value = data; loading.value = false; }); } return { user, loading, fetchUser }; }
D. function useUser() { const user = ref(null); const loading = ref(true); fetch('/api/user').then(r => r.json()).then(data => { user = data; loading.value = false; }); return { user, loading }; }

Solution

  1. Step 1: Check reactive state initialization

    function useUser() { const user = ref(null); const loading = ref(true); async function fetchUser() { loading.value = true; user.value = await fetch('/api/user').then(r => r.json()); loading.value = false; } fetchUser(); return { user, loading }; } correctly uses ref for user and loading states.
  2. Step 2: Verify async fetch and state updates

    function useUser() { const user = ref(null); const loading = ref(true); async function fetchUser() { loading.value = true; user.value = await fetch('/api/user').then(r => r.json()); loading.value = false; } fetchUser(); return { user, loading }; } uses async function to set loading true, await fetch, then set loading false.
  3. Step 3: Confirm fetch is called inside composable

    function useUser() { const user = ref(null); const loading = ref(true); async function fetchUser() { loading.value = true; user.value = await fetch('/api/user').then(r => r.json()); loading.value = false; } fetchUser(); return { user, loading }; } calls fetchUser() immediately to start loading data.
  4. Final Answer:

    function useUser() { const user = ref(null); const loading = ref(true); async function fetchUser() { loading.value = true; user.value = await fetch('/api/user').then(r => r.json()); loading.value = false; } fetchUser(); return { user, loading }; } -> Option A
  5. Quick Check:

    Async fetch with reactive refs = A [OK]
Hint: Use ref for state and async function with await inside composable [OK]
Common Mistakes:
  • Reassigning reactive or ref variables directly
  • Not using async/await properly
  • Not updating loading state correctly
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard