Differences and Implementation Principles of Vue3's ref and reactive
Differences and Implementation Principles of Vue3's ref and reactive
Problem Description
Vue3's reactivity system provides two core APIs, ref and reactive, to create reactive data. Understanding their differences, applicable scenarios, and underlying implementation principles is crucial for correctly using Vue3 and handling interviews.
Knowledge Explanation
Step 1: Design Purpose and Basic Usage
-
reactive
- Design Goal: Used to create a deeply reactive object or array. It proxies the entire object via Proxy, recursively converting all properties of the object into reactive ones.
- Basic Usage:
import { reactive } from 'vue'; const state = reactive({ count: 0, user: { name: 'Alice' } }); // Modifying properties automatically updates the view state.count++; state.user.name = 'Bob'; // Nested properties are also reactive
-
ref
- Design Goal: Used to create a reactive reference that can wrap any value (including primitive types like string, number, boolean, as well as objects and arrays). It solves the problem that
reactivecannot directly proxy primitive types. - Basic Usage:
import { ref } from 'vue'; const count = ref(0); // Wrap a number const message = ref('Hello'); // Wrap a string const obj = ref({ name: 'Alice' }); // Wrap an object // Access and modification require the `.value` property console.log(count.value); // 0 count.value++; // Modify message.value = 'World'; obj.value.name = 'Bob'; // Note: Accessing properties on `.value` here - Automatic Unwrapping in Templates: In templates, you don't need to write
.value; Vue automatically unwraps it for you.<template> <div>{{ count }}</div> <!-- No need to write count.value --> </template>
- Design Goal: Used to create a reactive reference that can wrap any value (including primitive types like string, number, boolean, as well as objects and arrays). It solves the problem that
Step 2: Core Differences Comparison
| Feature | ref |
reactive |
|---|---|---|
| Data Type | Can wrap any value (primitive types and reference types) | Can only be used for object types (Object, Array, Map, Set) |
| Access/Modify | Requires the .value property |
Can directly access and modify properties |
| Template Usage | Automatic unwrapping, no .value needed |
Direct property access |
| TS Type Definition | Ref<T> |
The object itself is reactive, more intuitive typing |
| Implementation Principle | Implemented via getter/setter for the .value property of an object |
Proxies the entire object via Proxy |
Step 3: Underlying Implementation Principle Analysis
-
Core Implementation of reactive
- Technology: Based on ES6's
Proxy. - Process:
reactive(obj)returns a Proxy wrapper forobj.- Getter Tracking (Dependency Collection): When you read a property of the proxy object (e.g.,
state.count), the Proxy'sgettrap function is triggered. Vue executestrack(target, key)here, "collecting" the currently running side effect function (e.g., the component's render function) to establish a dependency relationship. This is called dependency collection. - Setter Triggering Updates: When you modify a property of the proxy object (e.g.,
state.count = 1), the Proxy'ssettrap function is triggered. Vue executestrigger(target, key)here, finding all side effect functions that depend on this property and re-executing them. This is called triggering updates.
- Recursive Reactivity: In the
gettrap, if the read property value is itself an object, Vue recursively callsreactive()on it, ensuring nested properties are also reactive.
- Technology: Based on ES6's
-
Core Implementation of ref
- Technology: Based on property accessors (getter/setter) of a plain object.
- Process:
ref(value)returns a plain object called a "ref object" with the structure{ value: ... }.- This ref object has a private property
_valueto store the actual value. - It exposes a public
valueproperty. Reading and modifying thisvalueproperty are intercepted by getter and setter. - Getter Tracking (Dependency Collection): When you read
myRef.value, the getter function is called. Internally, it executestrack(ref, 'value'), tracking access to the key'value'. - Setter Triggering Updates: When you set
myRef.value = newValue, the setter function is called. Internally, it first updates_value, then executestrigger(ref, 'value'), notifying all dependencies to update.
- Optimization for ref: If an object is passed to
ref, Vue internally automatically callsreactiveto deeply process this object. Therefore,ref({})is equivalent toreactive({})in terms of reactivity, differing only in the access method (requires.value).
Step 4: How to Choose and Summary
-
Scenarios for using
ref:- Defining reactive data for primitive types (e.g., strings, numbers, booleans).
- Defining reactive data whose future value is uncertain. Because
refis more versatile. - When returning reactive data in logic reuse (Composables),
refis easier to destructure while maintaining reactivity.
-
Scenarios for using
reactive:- Defining a set of logically related data, such as a form object, a page state management object. This way, you don't need to add
.valuebefore each property, making the code cleaner.
- Defining a set of logically related data, such as a form object, a page state management object. This way, you don't need to add
-
Key Points to Remember:
- The limitation of
reactiveis that it only works on object types and uses Proxy. - The universality of
refcomes from its use of getter/setter to wrap the.valueproperty, allowing it to hold any value. - Both ultimately integrate into Vue3's unified reactivity system (
trackandtrigger).
- The limitation of