Mechanisms Behind Vue.js $set and $delete Operations

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:

  1. Adding a new key: object.newProp = value
  2. 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:

  1. Arrays use splice to handle index-based insertion while maintaining reactivity.
  2. Existing keys simply update values.
  3. Missing keys require makeReactive to define getters/seters.
  4. 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:

  1. Arrays rely on native splice methods for removal notifications.
  2. The existence of the key is verified before proceeding.
  3. Native delete removes the descriptor from the object.
  4. If the target is reactive, the change subscription list is updated.

Tags: vuejs Reactivity javascript-objects source-code-analysis

Posted on Wed, 14 Oct 2026 16:56:25 +0000 by OmegaB