src/core/observer/index.ts TYPESCRIPT 340 lines View on github.com → Search inside
1import Dep from './dep'2import VNode from '../vdom/vnode'3import { arrayMethods } from './array'4import {5  def,6  warn,7  hasOwn,8  isArray,9  hasProto,10  isPlainObject,11  isPrimitive,12  isUndef,13  isValidArrayIndex,14  isServerRendering,15  hasChanged,16  noop17} from '../util/index'18import { isReadonly, isRef, TrackOpTypes, TriggerOpTypes } from '../../v3'1920const arrayKeys = Object.getOwnPropertyNames(arrayMethods)2122const NO_INITIAL_VALUE = {}2324/**25 * In some cases we may want to disable observation inside a component's26 * update computation.27 */28export let shouldObserve: boolean = true2930export function toggleObserving(value: boolean) {31  shouldObserve = value32}3334// ssr mock dep35const mockDep = {36  notify: noop,37  depend: noop,38  addSub: noop,39  removeSub: noop40} as Dep4142/**43 * Observer class that is attached to each observed44 * object. Once attached, the observer converts the target45 * object's property keys into getter/setters that46 * collect dependencies and dispatch updates.47 */48export class Observer {49  dep: Dep50  vmCount: number // number of vms that have this object as root $data5152  constructor(public value: any, public shallow = false, public mock = false) {53    // this.value = value54    this.dep = mock ? mockDep : new Dep()55    this.vmCount = 056    def(value, '__ob__', this)57    if (isArray(value)) {58      if (!mock) {59        if (hasProto) {60          /* eslint-disable no-proto */61          ;(value as any).__proto__ = arrayMethods62          /* eslint-enable no-proto */63        } else {64          for (let i = 0, l = arrayKeys.length; i < l; i++) {65            const key = arrayKeys[i]66            def(value, key, arrayMethods[key])67          }68        }69      }70      if (!shallow) {71        this.observeArray(value)72      }73    } else {74      /**75       * Walk through all properties and convert them into76       * getter/setters. This method should only be called when77       * value type is Object.78       */79      const keys = Object.keys(value)80      for (let i = 0; i < keys.length; i++) {81        const key = keys[i]82        defineReactive(value, key, NO_INITIAL_VALUE, undefined, shallow, mock)83      }84    }85  }8687  /**88   * Observe a list of Array items.89   */90  observeArray(value: any[]) {91    for (let i = 0, l = value.length; i < l; i++) {92      observe(value[i], false, this.mock)93    }94  }95}9697// helpers9899/**100 * Attempt to create an observer instance for a value,101 * returns the new observer if successfully observed,102 * or the existing observer if the value already has one.103 */104export function observe(105  value: any,106  shallow?: boolean,107  ssrMockReactivity?: boolean108): Observer | void {109  if (value && hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {110    return value.__ob__111  }112  if (113    shouldObserve &&114    (ssrMockReactivity || !isServerRendering()) &&115    (isArray(value) || isPlainObject(value)) &&116    Object.isExtensible(value) &&117    !value.__v_skip /* ReactiveFlags.SKIP */ &&118    !isRef(value) &&119    !(value instanceof VNode)120  ) {121    return new Observer(value, shallow, ssrMockReactivity)122  }123}124125/**126 * Define a reactive property on an Object.127 */128export function defineReactive(129  obj: object,130  key: string,131  val?: any,132  customSetter?: Function | null,133  shallow?: boolean,134  mock?: boolean,135  observeEvenIfShallow = false136) {137  const dep = new Dep()138139  const property = Object.getOwnPropertyDescriptor(obj, key)140  if (property && property.configurable === false) {141    return142  }143144  // cater for pre-defined getter/setters145  const getter = property && property.get146  const setter = property && property.set147  if (148    (!getter || setter) &&149    (val === NO_INITIAL_VALUE || arguments.length === 2)150  ) {151    val = obj[key]152  }153154  let childOb = shallow ? val && val.__ob__ : observe(val, false, mock)155  Object.defineProperty(obj, key, {156    enumerable: true,157    configurable: true,158    get: function reactiveGetter() {159      const value = getter ? getter.call(obj) : val160      if (Dep.target) {161        if (__DEV__) {162          dep.depend({163            target: obj,164            type: TrackOpTypes.GET,165            key166          })167        } else {168          dep.depend()169        }170        if (childOb) {171          childOb.dep.depend()172          if (isArray(value)) {173            dependArray(value)174          }175        }176      }177      return isRef(value) && !shallow ? value.value : value178    },179    set: function reactiveSetter(newVal) {180      const value = getter ? getter.call(obj) : val181      if (!hasChanged(value, newVal)) {182        return183      }184      if (__DEV__ && customSetter) {185        customSetter()186      }187      if (setter) {188        setter.call(obj, newVal)189      } else if (getter) {190        // #7981: for accessor properties without setter191        return192      } else if (!shallow && isRef(value) && !isRef(newVal)) {193        value.value = newVal194        return195      } else {196        val = newVal197      }198      childOb = shallow ? newVal && newVal.__ob__ : observe(newVal, false, mock)199      if (__DEV__) {200        dep.notify({201          type: TriggerOpTypes.SET,202          target: obj,203          key,204          newValue: newVal,205          oldValue: value206        })207      } else {208        dep.notify()209      }210    }211  })212213  return dep214}215216/**217 * Set a property on an object. Adds the new property and218 * triggers change notification if the property doesn't219 * already exist.220 */221export function set<T>(array: T[], key: number, value: T): T222export function set<T>(object: object, key: string | number, value: T): T223export function set(224  target: any[] | Record<string, any>,225  key: any,226  val: any227): any {228  if (__DEV__ && (isUndef(target) || isPrimitive(target))) {229    warn(230      `Cannot set reactive property on undefined, null, or primitive value: ${target}`231    )232  }233  if (isReadonly(target)) {234    __DEV__ && warn(`Set operation on key "${key}" failed: target is readonly.`)235    return236  }237  const ob = (target as any).__ob__238  if (isArray(target) && isValidArrayIndex(key)) {239    target.length = Math.max(target.length, key)240    target.splice(key, 1, val)241    // when mocking for SSR, array methods are not hijacked242    if (ob && !ob.shallow && ob.mock) {243      observe(val, false, true)244    }245    return val246  }247  if (key in target && !(key in Object.prototype)) {248    target[key] = val249    return val250  }251  if ((target as any)._isVue || (ob && ob.vmCount)) {252    __DEV__ &&253      warn(254        'Avoid adding reactive properties to a Vue instance or its root $data ' +255          'at runtime - declare it upfront in the data option.'256      )257    return val258  }259  if (!ob) {260    target[key] = val261    return val262  }263  defineReactive(ob.value, key, val, undefined, ob.shallow, ob.mock)264  if (__DEV__) {265    ob.dep.notify({266      type: TriggerOpTypes.ADD,267      target: target,268      key,269      newValue: val,270      oldValue: undefined271    })272  } else {273    ob.dep.notify()274  }275  return val276}277278/**279 * Delete a property and trigger change if necessary.280 */281export function del<T>(array: T[], key: number): void282export function del(object: object, key: string | number): void283export function del(target: any[] | object, key: any) {284  if (__DEV__ && (isUndef(target) || isPrimitive(target))) {285    warn(286      `Cannot delete reactive property on undefined, null, or primitive value: ${target}`287    )288  }289  if (isArray(target) && isValidArrayIndex(key)) {290    target.splice(key, 1)291    return292  }293  const ob = (target as any).__ob__294  if ((target as any)._isVue || (ob && ob.vmCount)) {295    __DEV__ &&296      warn(297        'Avoid deleting properties on a Vue instance or its root $data ' +298          '- just set it to null.'299      )300    return301  }302  if (isReadonly(target)) {303    __DEV__ &&304      warn(`Delete operation on key "${key}" failed: target is readonly.`)305    return306  }307  if (!hasOwn(target, key)) {308    return309  }310  delete target[key]311  if (!ob) {312    return313  }314  if (__DEV__) {315    ob.dep.notify({316      type: TriggerOpTypes.DELETE,317      target: target,318      key319    })320  } else {321    ob.dep.notify()322  }323}324325/**326 * Collect dependencies on array elements when the array is touched, since327 * we cannot intercept array element access like property getters.328 */329function dependArray(value: Array<any>) {330  for (let e, i = 0, l = value.length; i < l; i++) {331    e = value[i]332    if (e && e.__ob__) {333      e.__ob__.dep.depend()334    }335    if (isArray(e)) {336      dependArray(e)337    }338  }339}

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.