--- title: Use shallowRef for Large Objects and External Data impact: MEDIUM impactDescription: Deep reactivity on large objects causes performance overhead - shallowRef only tracks .value changes type: efficiency tags: [vue3, reactivity, shallowRef, performance, optimization] --- # Use shallowRef for Large Objects and External Data **Impact: MEDIUM** - By default, `ref()` makes objects deeply reactive, wrapping all nested properties in Proxies. For large data structures, external libraries, or immutable data, this causes unnecessary overhead. Use `shallowRef()` to only track `.value` changes. `shallowRef()` is ideal for large datasets from APIs, class instances, DOM nodes, or objects managed by external libraries. Vue only tracks when `.value` is replaced, not internal mutations, significantly reducing reactivity overhead. ## Task Checklist - [ ] Use `shallowRef()` for large API response data that won't be mutated - [ ] Use `shallowRef()` for external library objects (maps, charts, etc.) - [ ] Use `shallowRef()` for class instances with their own state management - [ ] Use `markRaw()` for objects that should never be reactive (e.g., third-party instances) - [ ] Remember: with shallowRef, you must replace `.value` entirely to trigger updates **Incorrect:** ```javascript import { ref } from 'vue' // INEFFICIENT: Deep reactivity on large dataset const users = ref(await fetchUsers()) // 10,000 users, each deeply reactive // INEFFICIENT: External library wrapped in Proxy const mapInstance = ref(new mapboxgl.Map({ /* ... */ })) // INEFFICIENT: Large immutable API response const apiResponse = ref(await fetch('/api/big-data').then(r => r.json())) ``` **Correct:** ```javascript import { shallowRef, markRaw, triggerRef } from 'vue' // EFFICIENT: Only .value replacement is tracked const users = shallowRef(await fetchUsers()) // Update by replacing the entire array users.value = await fetchUsers() // If you mutate and need to trigger update, use triggerRef users.value.push(newUser) triggerRef(users) // Manually trigger watchers // EFFICIENT: External library object const mapInstance = shallowRef(null) onMounted(() => { mapInstance.value = new mapboxgl.Map({ /* ... */ }) }) // BEST for objects that should NEVER be reactive const thirdPartyLib = markRaw(new SomeLibrary()) // This object won't be wrapped in Proxy even if used in reactive() ``` ```vue ``` ```javascript // Comparison: ref() vs shallowRef() // With ref(): Vue wraps EVERY nested property const deep = ref({ level1: { level2: { level3: { value: 1 } } } }) deep.value.level1.level2.level3.value++ // Tracked! // With shallowRef(): Only .value is tracked const shallow = shallowRef({ level1: { level2: { level3: { value: 1 } } } }) shallow.value.level1.level2.level3.value++ // NOT tracked! shallow.value = { /* new object */ } // Tracked! ``` ## Reference - [Vue.js Reactivity Fundamentals - Reducing Reactivity Overhead](https://vuejs.org/guide/best-practices/performance.html#reduce-reactivity-overhead-for-large-immutable-structures) - [Vue.js shallowRef API](https://vuejs.org/api/reactivity-advanced.html#shallowref) - [Vue.js markRaw API](https://vuejs.org/api/reactivity-advanced.html#markraw)