Vue's reactivity system primarily depends on Object.defineProperty for interception alongside overridden array prototypes. While this approach effectively tracks read and write operations, it cannot detect when new properties are assigned or existing ones are removed.
Limitations of Direct Assignment
Standard JavaScript operations fail to trigger updates in certain scenarios:
- Adding a new key:
object.newProp = value - Removing a key:
delete object.existingProp
To resolve these gaps, the framework exposes specific API methods. Instead of using native syntax for modification in reactive contexts, developers should utilize these dedicated functions to ensure dependency tracking is activated correctly.
The implementation strategy mirrors array prototype interception by wrapping native behaviors with custom logic. This allows the system to intercept the operation and notify dependent watchers.
Prototype Registration
These methods are attached to the instance prototype during initialization.
Core Instance Configuration
In the main constructor file, the setup process includes mounting state management utilities:
function ComponentInstance (config) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof ComponentInstance)
) {
console.warn('ComponentInstance must be initialized with `new` keyword')
}
this._init(config)
}
// Registering state manipulation methods
attachStateMethods(ComponentInstance)
export default ComponentInstance
The attachment happens through the mixin function responsible for state handling.
State Mixin Implementation
Inside the core configuration module, the methods reference their internal implementations:
import {
setProperty,
removeProperty
} from '../observer/module'
export function attachStateMethods (ClassRef) {
ClassRef.prototype.$set = setProperty
ClassRef.prototype.$delete = removeProperty
}
The actual logic resides within the observer module.
Implementing Property Addition ($set)
The underlying mechanism handles different data types differently. Reactive objects and arrays include a internal reference (__ob__) linked to the observer instance.
For new properties on objects, the system re-tracks them using reactive definitions. For arrays, index modifications trigger internal splice operations to update dependencies.
Logic Breakdown
/**
* Set property on target and trigger notification
*/
export function setProperty (target, propertyKey, newValue) {
// Handle Array Index Updates
if (Array.isArray(target) && isValidIndex(propertyKey)) {
target.length = Math.max(target.length, Number(propertyKey)) + 1
target.splice(Number(propertyKey), 1, newValue)
return newValue
}
// Handle Existing Properties
if (hasOwnProperty(target, propertyKey)) {
target[propertyKey] = newValue
return newValue
}
const observer = (target).__ob__
// Non-reactive Targets
if (!observer) {
target[propertyKey] = newValue
return newValue
}
// New Reactive Tracking
makeReactive(observer.value, propertyKey, newValue)
observer.dep.notify()
return newValue
}
Key logic points:
- Arrays use
spliceto handle index-based insertion while maintaining reactivity. - Existing keys simply update values.
- Missing keys require
makeReactiveto define getters/seters. - The observer's dependency collection group is notified to trigger UI updates.
Implementing Property Deletion ($delete)
Similar to setting values, deletion requires specific intervention to track changes.
/**
* Remove property from target and trigger notification
*/
export function removeProperty (target, propertyKey) {
// Handle Array Index Removal
if (Array.isArray(target) && isValidIndex(propertyKey)) {
target.splice(Number(propertyKey), 1)
return
}
const observer = (target).__ob__
// Skip if Key Does Not Exist
if (!hasOwnProperty(target, propertyKey)) {
return
}
// Perform Native Deletion
delete target[propertyKey]
// Notify Observers If Reactive
if (!observer) {
return
}
observer.dep.notify()
}
Deletion flow:
- Arrays rely on native splice methods for removal notifications.
- The existence of the key is verified before proceeding.
- Native
deleteremoves the descriptor from the object. - If the target is reactive, the change subscription list is updated.