aboutsummaryrefslogtreecommitdiff
path: root/node_modules/vue/src/core/observer/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/vue/src/core/observer/index.js')
-rw-r--r--node_modules/vue/src/core/observer/index.js273
1 files changed, 273 insertions, 0 deletions
diff --git a/node_modules/vue/src/core/observer/index.js b/node_modules/vue/src/core/observer/index.js
new file mode 100644
index 00000000..4a030ed7
--- /dev/null
+++ b/node_modules/vue/src/core/observer/index.js
@@ -0,0 +1,273 @@
+/* @flow */
+
+import Dep from './dep'
+import VNode from '../vdom/vnode'
+import { arrayMethods } from './array'
+import {
+ def,
+ warn,
+ hasOwn,
+ hasProto,
+ isObject,
+ isPlainObject,
+ isPrimitive,
+ isUndef,
+ isValidArrayIndex,
+ isServerRendering
+} from '../util/index'
+
+const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
+
+/**
+ * In some cases we may want to disable observation inside a component's
+ * update computation.
+ */
+export let shouldObserve: boolean = true
+
+export function toggleObserving (value: boolean) {
+ shouldObserve = value
+}
+
+/**
+ * Observer class that is attached to each observed
+ * object. Once attached, the observer converts the target
+ * object's property keys into getter/setters that
+ * collect dependencies and dispatch updates.
+ */
+export class Observer {
+ value: any;
+ dep: Dep;
+ vmCount: number; // number of vms that has this object as root $data
+
+ constructor (value: any) {
+ this.value = value
+ this.dep = new Dep()
+ this.vmCount = 0
+ def(value, '__ob__', this)
+ if (Array.isArray(value)) {
+ const augment = hasProto
+ ? protoAugment
+ : copyAugment
+ augment(value, arrayMethods, arrayKeys)
+ this.observeArray(value)
+ } else {
+ this.walk(value)
+ }
+ }
+
+ /**
+ * Walk through each property and convert them into
+ * getter/setters. This method should only be called when
+ * value type is Object.
+ */
+ walk (obj: Object) {
+ const keys = Object.keys(obj)
+ for (let i = 0; i < keys.length; i++) {
+ defineReactive(obj, keys[i])
+ }
+ }
+
+ /**
+ * Observe a list of Array items.
+ */
+ observeArray (items: Array<any>) {
+ for (let i = 0, l = items.length; i < l; i++) {
+ observe(items[i])
+ }
+ }
+}
+
+// helpers
+
+/**
+ * Augment an target Object or Array by intercepting
+ * the prototype chain using __proto__
+ */
+function protoAugment (target, src: Object, keys: any) {
+ /* eslint-disable no-proto */
+ target.__proto__ = src
+ /* eslint-enable no-proto */
+}
+
+/**
+ * Augment an target Object or Array by defining
+ * hidden properties.
+ */
+/* istanbul ignore next */
+function copyAugment (target: Object, src: Object, keys: Array<string>) {
+ for (let i = 0, l = keys.length; i < l; i++) {
+ const key = keys[i]
+ def(target, key, src[key])
+ }
+}
+
+/**
+ * Attempt to create an observer instance for a value,
+ * returns the new observer if successfully observed,
+ * or the existing observer if the value already has one.
+ */
+export function observe (value: any, asRootData: ?boolean): Observer | void {
+ if (!isObject(value) || value instanceof VNode) {
+ return
+ }
+ let ob: Observer | void
+ if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
+ ob = value.__ob__
+ } else if (
+ shouldObserve &&
+ !isServerRendering() &&
+ (Array.isArray(value) || isPlainObject(value)) &&
+ Object.isExtensible(value) &&
+ !value._isVue
+ ) {
+ ob = new Observer(value)
+ }
+ if (asRootData && ob) {
+ ob.vmCount++
+ }
+ return ob
+}
+
+/**
+ * Define a reactive property on an Object.
+ */
+export function defineReactive (
+ obj: Object,
+ key: string,
+ val: any,
+ customSetter?: ?Function,
+ shallow?: boolean
+) {
+ const dep = new Dep()
+
+ const property = Object.getOwnPropertyDescriptor(obj, key)
+ if (property && property.configurable === false) {
+ return
+ }
+
+ // cater for pre-defined getter/setters
+ const getter = property && property.get
+ if (!getter && arguments.length === 2) {
+ val = obj[key]
+ }
+ const setter = property && property.set
+
+ let childOb = !shallow && observe(val)
+ Object.defineProperty(obj, key, {
+ enumerable: true,
+ configurable: true,
+ get: function reactiveGetter () {
+ const value = getter ? getter.call(obj) : val
+ if (Dep.target) {
+ dep.depend()
+ if (childOb) {
+ childOb.dep.depend()
+ if (Array.isArray(value)) {
+ dependArray(value)
+ }
+ }
+ }
+ return value
+ },
+ set: function reactiveSetter (newVal) {
+ const value = getter ? getter.call(obj) : val
+ /* eslint-disable no-self-compare */
+ if (newVal === value || (newVal !== newVal && value !== value)) {
+ return
+ }
+ /* eslint-enable no-self-compare */
+ if (process.env.NODE_ENV !== 'production' && customSetter) {
+ customSetter()
+ }
+ if (setter) {
+ setter.call(obj, newVal)
+ } else {
+ val = newVal
+ }
+ childOb = !shallow && observe(newVal)
+ dep.notify()
+ }
+ })
+}
+
+/**
+ * Set a property on an object. Adds the new property and
+ * triggers change notification if the property doesn't
+ * already exist.
+ */
+export function set (target: Array<any> | Object, key: any, val: any): any {
+ if (process.env.NODE_ENV !== 'production' &&
+ (isUndef(target) || isPrimitive(target))
+ ) {
+ warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
+ }
+ if (Array.isArray(target) && isValidArrayIndex(key)) {
+ target.length = Math.max(target.length, key)
+ target.splice(key, 1, val)
+ return val
+ }
+ if (key in target && !(key in Object.prototype)) {
+ target[key] = val
+ return val
+ }
+ const ob = (target: any).__ob__
+ if (target._isVue || (ob && ob.vmCount)) {
+ process.env.NODE_ENV !== 'production' && warn(
+ 'Avoid adding reactive properties to a Vue instance or its root $data ' +
+ 'at runtime - declare it upfront in the data option.'
+ )
+ return val
+ }
+ if (!ob) {
+ target[key] = val
+ return val
+ }
+ defineReactive(ob.value, key, val)
+ ob.dep.notify()
+ return val
+}
+
+/**
+ * Delete a property and trigger change if necessary.
+ */
+export function del (target: Array<any> | Object, key: any) {
+ if (process.env.NODE_ENV !== 'production' &&
+ (isUndef(target) || isPrimitive(target))
+ ) {
+ warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)
+ }
+ if (Array.isArray(target) && isValidArrayIndex(key)) {
+ target.splice(key, 1)
+ return
+ }
+ const ob = (target: any).__ob__
+ if (target._isVue || (ob && ob.vmCount)) {
+ process.env.NODE_ENV !== 'production' && warn(
+ 'Avoid deleting properties on a Vue instance or its root $data ' +
+ '- just set it to null.'
+ )
+ return
+ }
+ if (!hasOwn(target, key)) {
+ return
+ }
+ delete target[key]
+ if (!ob) {
+ return
+ }
+ ob.dep.notify()
+}
+
+/**
+ * Collect dependencies on array elements when the array is touched, since
+ * we cannot intercept array element access like property getters.
+ */
+function dependArray (value: Array<any>) {
+ for (let e, i = 0, l = value.length; i < l; i++) {
+ e = value[i]
+ e && e.__ob__ && e.__ob__.dep.depend()
+ if (Array.isArray(e)) {
+ dependArray(e)
+ }
+ }
+}