The final stage before deploying NOVA LAB systems on Mars - let's learn proven patterns used in production Vue 3 applications.
In large projects, a clear file structure is crucial.
1src/
2 composables/
3 useAuth.ts # Authorization
4 useNotifications.ts # Notifications
5 useSensors.ts # Sensor data
6 useTheme.ts # App theme
7 index.ts # Re-export all composables1// composables/index.ts - central export point
2export { useAuth } from './useAuth'
3export { useNotifications } from './useNotifications'
4export { useSensors } from './useSensors'
5export { useTheme } from './useTheme'
6
7// Import in component - clean and readable
8import { useAuth, useNotifications } from '@/composables'TypeScript ensures type safety and better IDE suggestions.
1import { ref, computed, Ref, ComputedRef } from 'vue'
2
3// Interface for returned API
4interface UsePaginationReturn {
5 currentPage: Ref<number>
6 totalPages: ComputedRef<number>
7 paginatedItems: ComputedRef<any[]>
8 goToPage: (page: number) => void
9 nextPage: () => void
10 prevPage: () => void
11}
12
13// Typed composable
14function usePagination<T>(
15 items: Ref<T[]>,
16 perPage: number = 10
17): UsePaginationReturn {
18 const currentPage = ref(1)
19
20 const totalPages = computed(() =>
21 Math.ceil(items.value.length / perPage)
22 )
23
24 const paginatedItems = computed(() => {
25 const start = (currentPage.value - 1) * perPage
26 return items.value.slice(start, start + perPage)
27 })
28
29 function goToPage(page: number) {
30 if (page >= 1 && page <= totalPages.value) {
31 currentPage.value = page
32 }
33 }
34
35 function nextPage() { goToPage(currentPage.value + 1) }
36 function prevPage() { goToPage(currentPage.value - 1) }
37
38 return {
39 currentPage,
40 totalPages,
41 paginatedItems,
42 goToPage,
43 nextPage,
44 prevPage
45 }
46}The options pattern allows flexible composable configuration - like configuration panels for NOVA LAB modules.
1import { ref, watch, onUnmounted } from 'vue'
2
3interface UsePollingOptions {
4 interval?: number
5 immediate?: boolean
6 retryOnError?: boolean
7 maxRetries?: number
8 onError?: (error: Error) => void
9}
10
11function usePolling(
12 fetchFn: () => Promise<any>,
13 options: UsePollingOptions = {}
14) {
15 const {
16 interval = 5000,
17 immediate = true,
18 retryOnError = true,
19 maxRetries = 3,
20 onError
21 } = options
22
23 const data = ref(null)
24 const error = ref(null)
25 const isPolling = ref(false)
26 const retryCount = ref(0)
27 let timerId: number | null = null
28
29 async function poll() {
30 try {
31 data.value = await fetchFn()
32 error.value = null
33 retryCount.value = 0
34 } catch (e) {
35 error.value = e
36 onError?.(e)
37
38 if (!retryOnError || retryCount.value >= maxRetries) {
39 stopPolling()
40 return
41 }
42 retryCount.value++
43 }
44 }
45
46 function startPolling() {
47 if (isPolling.value) return
48 isPolling.value = true
49 if (immediate) poll()
50 timerId = setInterval(poll, interval)
51 }
52
53 function stopPolling() {
54 isPolling.value = false
55 if (timerId) {
56 clearInterval(timerId)
57 timerId = null
58 }
59 }
60
61 onUnmounted(stopPolling)
62
63 return { data, error, isPolling, retryCount, startPolling, stopPolling }
64}
65
66// Usage with options
67const sensorData = usePolling(
68 () => fetch('/api/sensors').then(r => r.json()),
69 {
70 interval: 3000,
71 immediate: true,
72 maxRetries: 5,
73 onError: (e) => console.error('Polling failed:', e)
74 }
75)In production applications we often need shared state without prop drilling.
1import { ref, computed, provide, inject, readonly } from 'vue'
2
3const StoreKey = Symbol('AppStore')
4
5// Created once in the root component (App.vue)
6function createAppStore() {
7 const user = ref(null)
8 const isAuthenticated = computed(() => user.value !== null)
9 const notifications = ref([])
10
11 function login(userData) { user.value = userData }
12 function logout() { user.value = null }
13 function notify(message, type = 'info') {
14 notifications.value.push({
15 id: Date.now(),
16 message,
17 type,
18 timestamp: new Date()
19 })
20 }
21
22 const store = {
23 user: readonly(user),
24 isAuthenticated,
25 notifications: readonly(notifications),
26 login,
27 logout,
28 notify
29 }
30
31 provide(StoreKey, store)
32 return store
33}
34
35// Used in any nested component
36function useAppStore() {
37 const store = inject(StoreKey)
38 if (!store) {
39 throw new Error('useAppStore requires createAppStore in App.vue')
40 }
41 return store
42}In this module you learned advanced techniques for working with composables:
provide/inject for shared state without prop drillinguseLocalStorage, useDark, useWindowSize)shallowRef for large data, computed caching, avoiding memory leaksThese techniques will allow you to build scalable and performant Vue 3 applications - ready for space missions!