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.