Using ref and reactive in the Vue 3 Composition API
In the Vue 3 Composition API, the core mechanism for implementing data-driven view updates is the reactivity system. Developers typically import these two key functions via import { ref, reactive } from 'vue' to create and manage state. While both aim to provide the same reactive experience—automatically triggering related computation logic and UI rendering when underlying data changes—the choice of which one to use in actual development scenarios often depends on the data type, nesting depth, and specific business requirements.ref (reference) is one of the most fundamental data reactivity tools in Vue 3. It accepts values of any type as arguments and returns a special object whose _value property stores the original data. This design allows ref to handle everything from simple scalar types to complex nested structures without worrying about whether the data type matches specific rules.
The typical pattern for using ref involves accessing and modifying internal values through destructuring operators (such as .value):
import { ref } from 'vue'
// Create a reactive number or string
const count = ref(0)
console.log(count.value) // Output: 0
count.value++
console.log(count.value) // Output: 1
The advantage of ref lies in its generality. Whether the data is a primitive type (such as Number, String, or Boolean) or an object, it works seamlessly. Furthermore, for top-level simple state management, ref provides a clear semantic model: the variable name represents the "reference," while .value explicitly indicates the specific content to be accessed.
reactive: Deeply Reactive Proxy Designed for Complex Objects
Unlike ref, reactive (reaction) is specifically designed to create deep reactivity proxies for objects or arrays. It returns a Proxy object capable of tracking internal property changes, additions, and deletions, automatically triggering dependency updates. This makes it more efficient and intuitive when handling large configuration objects, form data models, or multi-level nested structures.
When we need to manage a set of related attributes (such as user information or product details) or frequently modify the content of array elements, reactive is the more natural choice:
import { reactive } from 'vue'
// Create a reactive object
const user = reactive({
name: 'Alice',
age: 25,
})
user.name = 'Bob' // Vue automatically detects the change and updates the view
console.log(user.age) // Output: 25, no need for .value accessor
reactive performs exceptionally well when handling nested objects. It automatically recursively proxies internal attributes, meaning you do not need to manually expand every layer to modify data as you would with deep refs. For example, if an object containing multiple field configurations is created using reactive, changes to all sub-attributes are automatically captured.
Comparison and Selection Strategy Between the Two
Although both can achieve reactivity, making the correct choice is crucial in actual projects. Here are several key decision dimensions:
Data Type Match
- If the data is a primitive type (numbers, strings, etc.),
refis the most direct choice. Attempting to usereactive()on primitives is syntactically feasible but semantically less precise thanref.
- If the data is a primitive type (numbers, strings, etc.),
Nested Structure and Modification Frequency
- For large objects or arrays, especially in scenarios requiring frequent addition of new properties or deletion of elements (such as dynamic forms or complex configurations),
reactiveprovides a smoother API experience. When usingrefto handle such data, it often requires layer-by-layer destructuring via.value.propertyName = newValue, resulting in lower code readability and a higher risk of errors.
- For large objects or arrays, especially in scenarios requiring frequent addition of new properties or deletion of elements (such as dynamic forms or complex configurations),
Performance Considerations
- In extreme micro-optimization scenarios where only a single simple property is accessed and unnecessary deep proxy overhead needs to be avoided,
refmight be slightly lighter; however, in the vast majority of applications, this difference is negligible, and both perform perfectly to meet requirements.
- In extreme micro-optimization scenarios where only a single simple property is accessed and unnecessary deep proxy overhead needs to be avoided,
Comprehensive Example: Mixed Usage Scenario
In actual Vue component development, both tools are often combined to build state management strategies. The following is a typical combination case demonstrating how to utilize both ref and reactive simultaneously within the same component:
import { ref, reactive } from 'vue'
export default defineComponent({
setup() {
// Use ref to manage simple counters or flags
const isDarkMode = ref(false)
// Use reactive to manage complex form data objects
const formState = reactive({
username: '',
email: '',
settings: {
notifications: true,
themeColor: 'blue'
}
})
return {
isDarkMode,
formState
}
},
})
In this example, isDarkMode as a simple boolean flag is very suitable for ref, while formState and its internal nested objects leverage reactive to implement complete deep reactivity tracking. This mixed usage ensures code conciseness while maximizing the flexibility of the Vue 3 reactivity system.
Mastering the differences and application scenarios between ref and reactive is a critical step in deeply understanding data flow management within the Vue 3 Composition API. By reasonably selecting these two tools, developers can build more robust, maintainable, and high-performance single-page application architectures.