Răsfoiți Sursa

annotate observer

Evan You 10 ani în urmă
părinte
comite
a953bdeeb6

+ 2 - 3
src/compiler/events.js

@@ -1,4 +1,3 @@
-import { isArray } from 'shared/util'
 const simplePathRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/
 
 // keyCode aliases
@@ -31,7 +30,7 @@ export function genHandlers (events) {
 function genHandler (handler) {
   if (!handler) {
     return 'function(){}'
-  } else if (isArray(handler)) {
+  } else if (Array.isArray(handler)) {
     return `[${handler.map(genHandler).join(',')}]`
   } else if (!handler.modifiers) {
     return simplePathRE.test(handler.value)
@@ -51,7 +50,7 @@ function genHandler (handler) {
 
 function genKeyFilter (key) {
   const code = keyCodes[key]
-  if (isArray(code)) {
+  if (Array.isArray(code)) {
     return `if(${code.map(c => `$event.keyCode!==${c}`).join('&&')})return;`
   } else {
     return `if($event.keyCode!==${code})return;`

+ 1 - 3
src/compiler/helpers.js

@@ -1,5 +1,3 @@
-import { isArray } from 'shared/util'
-
 export function baseWarn (msg) {
   console.error(`[Vue parser]: ${msg}`)
 }
@@ -46,7 +44,7 @@ export function addHandler (el, name, value, modifiers) {
   }
   const newHandler = { value, modifiers }
   const handlers = events[name]
-  if (isArray(handlers)) {
+  if (Array.isArray(handlers)) {
     handlers.push(newHandler)
   } else if (handlers) {
     events[name] = [handlers, newHandler]

+ 2 - 2
src/core/instance/render.js

@@ -5,7 +5,7 @@ import type VNode from '../vdom/vnode'
 import createElement from '../vdom/create-element'
 import { emptyVNode } from '../vdom/vnode'
 import { flatten } from '../vdom/helpers'
-import { bind, remove, isArray, isObject, renderString } from 'shared/util'
+import { bind, remove, isObject, renderString } from 'shared/util'
 import { resolveAsset, nextTick } from '../util/index'
 
 export const renderState = {
@@ -69,7 +69,7 @@ export function renderMixin (Vue: Class<Vue>) {
     render: () => VNode
   ): ?Array<VNode> {
     let ret: ?Array<VNode>, i, l, keys, key
-    if (isArray(val)) {
+    if (Array.isArray(val)) {
       ret = new Array(val.length)
       for (i = 0, l = val.length; i < l; i++) {
         ret[i] = render(val[i], i, i)

+ 1 - 2
src/core/instance/state.js

@@ -12,7 +12,6 @@ import {
 import {
   warn,
   hasOwn,
-  isArray,
   isPlainObject,
   bind,
   validateProp,
@@ -122,7 +121,7 @@ function initWatch (vm) {
   if (watch) {
     for (const key in watch) {
       const handler = watch[key]
-      if (isArray(handler)) {
+      if (Array.isArray(handler)) {
         for (let i = 0; i < handler.length; i++) {
           createWatcher(vm, key, handler[i])
         }

+ 5 - 0
src/core/observer/array.js

@@ -1,3 +1,8 @@
+/*
+ * not type checking this file because flow doesn't play well with
+ * dynamically accessing methods on Array prototype
+ */
+
 import { def } from '../util/index'
 
 const arrayProto = Array.prototype

+ 12 - 5
src/core/observer/dep.js

@@ -1,3 +1,6 @@
+/* @flow */
+
+import type Watcher from './watcher'
 import { remove } from '../util/index'
 
 let uid = 0
@@ -5,25 +8,29 @@ let uid = 0
 /**
  * A dep is an observable that can have multiple
  * directives subscribing to it.
- *
- * @constructor
  */
 export default class Dep {
+  static target: ?Watcher;
+  id: number;
+  subs: Array<Watcher>;
+
   constructor () {
     this.id = uid++
     this.subs = []
   }
 
-  addSub (sub) {
+  addSub (sub: Watcher) {
     this.subs.push(sub)
   }
 
-  removeSub (sub) {
+  removeSub (sub: Watcher) {
     remove(this.subs, sub)
   }
 
   depend () {
-    Dep.target.addDep(this)
+    if (Dep.target) {
+      Dep.target.addDep(this)
+    }
   }
 
   notify () {

+ 27 - 50
src/core/observer/index.js

@@ -1,10 +1,12 @@
+/* @flow */
+
+import type Vue from '../instance/index'
 import config from '../config'
 import Dep from './dep'
 import { arrayMethods } from './array'
 import {
   def,
   remove,
-  isArray,
   isObject,
   isPlainObject,
   hasProto,
@@ -30,17 +32,18 @@ export const observerState = {
  * object. Once attached, the observer converts target
  * object's property keys into getter/setters that
  * collect dependencies and dispatches updates.
- *
- * @param {Array|Object} value
- * @constructor
  */
 export class Observer {
-  constructor (value) {
+  value: any;
+  dep: Dep;
+  vms: ?Array<Vue>;
+
+  constructor (value: any) {
     this.value = value
     this.dep = new Dep()
     this.vms = null
     def(value, '__ob__', this)
-    if (isArray(value)) {
+    if (Array.isArray(value)) {
       const augment = hasProto
         ? protoAugment
         : copyAugment
@@ -55,10 +58,8 @@ export class Observer {
    * Walk through each property and convert them into
    * getter/setters. This method should only be called when
    * value type is Object.
-   *
-   * @param {Object} obj
    */
-  walk (obj) {
+  walk (obj: Object) {
     const val = this.value
     for (const key in obj) {
       defineReactive(val, key, obj[key])
@@ -67,10 +68,8 @@ export class Observer {
 
   /**
    * Observe a list of Array items.
-   *
-   * @param {Array} items
    */
-  observeArray (items) {
+  observeArray (items: Array<any>) {
     for (let i = 0, l = items.length; i < l; i++) {
       observe(items[i])
     }
@@ -81,20 +80,16 @@ export class Observer {
    * happen we can notify owner vms to proxy the keys and
    * digest the watchers. This is only called when the object
    * is observed as an instance's root $data.
-   *
-   * @param {Vue} vm
    */
-  addVm (vm) {
+  addVm (vm: Vue) {
     (this.vms || (this.vms = [])).push(vm)
   }
 
   /**
    * Remove an owner vm. This is called when the object is
    * swapped out as an instance's $data object.
-   *
-   * @param {Vue} vm
    */
-  removeVm (vm) {
+  removeVm (vm: Vue) {
     remove(this.vms, vm)
   }
 }
@@ -104,11 +99,8 @@ export class Observer {
 /**
  * Augment an target Object or Array by intercepting
  * the prototype chain using __proto__
- *
- * @param {Object|Array} target
- * @param {Object} src
  */
-function protoAugment (target, src) {
+function protoAugment (target, src: Object) {
   /* eslint-disable no-proto */
   target.__proto__ = src
   /* eslint-enable no-proto */
@@ -132,23 +124,18 @@ function copyAugment (target, src, keys) {
  * 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.
- *
- * @param {*} value
- * @param {Vue} [vm]
- * @return {Observer|undefined}
- * @static
  */
-export function observe (value, vm) {
+export function observe (value: any, vm?: Vue): Observer | void {
   if (!isObject(value)) {
     return
   }
-  let ob
+  let ob: Observer | void
   if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
     ob = value.__ob__
   } else if (
     observerState.shouldConvert &&
     !config._isServer &&
-    (isArray(value) || isPlainObject(value)) &&
+    (Array.isArray(value) || isPlainObject(value)) &&
     Object.isExtensible(value) &&
     !value._isVue
   ) {
@@ -162,12 +149,8 @@ export function observe (value, vm) {
 
 /**
  * Define a reactive property on an Object.
- *
- * @param {Object} obj
- * @param {String} key
- * @param {*} val
  */
-export function defineReactive (obj, key, val) {
+export function defineReactive (obj: Object, key: string, val: any) {
   const dep = new Dep()
 
   const property = Object.getOwnPropertyDescriptor(obj, key)
@@ -190,7 +173,7 @@ export function defineReactive (obj, key, val) {
         if (childOb) {
           childOb.dep.depend()
         }
-        if (isArray(value)) {
+        if (Array.isArray(value)) {
           for (let e, i = 0, l = value.length; i < l; i++) {
             e = value[i]
             e && e.__ob__ && e.__ob__.dep.depend()
@@ -219,15 +202,11 @@ export function defineReactive (obj, key, val) {
  * Set a property on an object. Adds the new property and
  * triggers change notification if the property doesn't
  * already exist.
- *
- * @param {Object} obj
- * @param {String} key
- * @param {*} val
- * @public
  */
-export function set (obj, key, val) {
-  if (isArray(obj)) {
-    return obj.splice(key, 1, val)
+export function set (obj: Array<any> | Object, key: any, val: any) {
+  if (Array.isArray(obj)) {
+    obj.splice(key, 1, val)
+    return val
   }
   if (hasOwn(obj, key)) {
     obj[key] = val
@@ -260,11 +239,8 @@ export function set (obj, key, val) {
 
 /**
  * Delete a property and trigger change if necessary.
- *
- * @param {Object} obj
- * @param {String} key
  */
-export function del (obj, key) {
+export function del (obj: Object, key: string) {
   if (obj._isVue) {
     process.env.NODE_ENV !== 'production' && warn(
       'Do not delete properties on a Vue instance - just set it to null.'
@@ -290,7 +266,7 @@ export function del (obj, key) {
   }
 }
 
-export function proxy (vm, key) {
+export function proxy (vm: Vue, key: string) {
   if (!isReserved(key)) {
     Object.defineProperty(vm, key, {
       configurable: true,
@@ -305,7 +281,8 @@ export function proxy (vm, key) {
   }
 }
 
-export function unproxy (vm, key) {
+// using Object type to avoid flow complaining
+export function unproxy (vm: Object, key: string) {
   if (!isReserved(key)) {
     delete vm[key]
   }

+ 11 - 15
src/core/observer/scheduler.js

@@ -1,3 +1,6 @@
+/* @flow */
+
+import type Watcher from './watcher'
 import config from '../config'
 import {
   warn,
@@ -11,10 +14,10 @@ import {
 // triggered, the DOM would have already been in updated
 // state.
 
-const queue = []
-const userQueue = []
-let has = {}
-let circular = {}
+const queue: Array<Watcher> = []
+const userQueue: Array<Watcher> = []
+let has: { [key: number]: ?true } = {}
+let circular: { [key: number]: number } = {}
 let waiting = false
 
 /**
@@ -51,16 +54,14 @@ function flushSchedulerQueue () {
  * pushed into the queue first and then its parent's props
  * changed.
  */
-function queueSorter (a, b) {
+function queueSorter (a: Watcher, b: Watcher) {
   return a.id - b.id
 }
 
 /**
  * Run the watchers in a single queue.
- *
- * @param {Array} queue
  */
-function runSchedulerQueue (queue) {
+function runSchedulerQueue (queue: Array<Watcher>) {
   // do not cache length because more watchers might be pushed
   // as we run existing watchers
   for (let i = 0; i < queue.length; i++) {
@@ -88,20 +89,15 @@ function runSchedulerQueue (queue) {
  * Push a watcher into the watcher queue.
  * Jobs with duplicate IDs will be skipped unless it's
  * pushed when the queue is being flushed.
- *
- * @param {Watcher} watcher
- *   properties:
- *   - {Number} id
- *   - {Function} run
  */
-export function queueWatcher (watcher) {
+export function queueWatcher (watcher: Watcher) {
   const id = watcher.id
   if (has[id] == null) {
     // push watcher into appropriate queue
     const q = watcher.user
       ? userQueue
       : queue
-    has[id] = q.length
+    has[id] = true
     q.push(watcher)
     // queue the flush
     if (!waiting) {

+ 34 - 35
src/core/observer/watcher.js

@@ -1,10 +1,11 @@
+/* @flow */
+
+import type Vue from '../instance/index'
 import Dep from './dep'
 import { queueWatcher } from './scheduler'
 import {
   warn,
   remove,
-  extend,
-  isArray,
   isObject,
   parsePath,
   _Set as Set
@@ -17,31 +18,37 @@ let prevTarget
  * A watcher parses an expression, collects dependencies,
  * and fires callback when the expression value changes.
  * This is used for both the $watch() api and directives.
- *
- * @param {Vue} vm
- * @param {String|Function} expOrFn
- * @param {Function} cb
- * @param {Object} options
- *                 - {Array} filters
- *                 - {Boolean} twoWay
- *                 - {Boolean} deep
- *                 - {Boolean} user
- *                 - {Boolean} sync
- *                 - {Boolean} lazy
- *                 - {Function} [preProcess]
- *                 - {Function} [postProcess]
- * @constructor
  */
 export default class Watcher {
-  constructor (vm, expOrFn, cb, options) {
-    // mix in options
-    if (options) {
-      extend(this, options)
-    }
-    const isFn = typeof expOrFn === 'function'
+  vm: Vue;
+  expression: string;
+  cb: Function;
+  id: number;
+  deep: boolean;
+  user: boolean;
+  lazy: boolean;
+  dirty: boolean;
+  active: boolean;
+  deps: Array<Dep>;
+  newDeps: Array<Dep>;
+  depIds: Set;
+  newDepIds: Set;
+  getter: Function;
+  value: any;
+
+  constructor (
+    vm: Vue,
+    expOrFn: string | Function,
+    cb: Function,
+    options?: Object = {}
+  ) {
     this.vm = vm
     vm._watchers.push(this)
-    this.expression = expOrFn
+    // options
+    this.deep = !!options.deep
+    this.user = !!options.user
+    this.lazy = !!options.lazy
+    this.expression = expOrFn.toString()
     this.cb = cb
     this.id = ++uid // uid for batching
     this.active = true
@@ -51,7 +58,7 @@ export default class Watcher {
     this.depIds = new Set()
     this.newDepIds = new Set()
     // parse expression for getter
-    if (isFn) {
+    if (typeof expOrFn === 'function') {
       this.getter = expOrFn
     } else {
       this.getter = parsePath(expOrFn)
@@ -95,10 +102,8 @@ export default class Watcher {
 
   /**
    * Add a dependency to this directive.
-   *
-   * @param {Dep} dep
    */
-  addDep (dep) {
+  addDep (dep: Dep) {
     const id = dep.id
     if (!this.newDepIds.has(id)) {
       this.newDepIds.add(id)
@@ -138,8 +143,6 @@ export default class Watcher {
   update () {
     if (this.lazy) {
       this.dirty = true
-    } else if (this.sync) {
-      this.run()
     } else {
       queueWatcher(this)
     }
@@ -208,7 +211,6 @@ export default class Watcher {
         this.deps[i].removeSub(this)
       }
       this.active = false
-      this.vm = this.cb = this.value = null
     }
   }
 }
@@ -217,18 +219,15 @@ export default class Watcher {
  * Recursively traverse an object to evoke all converted
  * getters, so that every nested property inside the object
  * is collected as a "deep" dependency.
- *
- * @param {*} val
- * @param {Set} seen
  */
 const seenObjects = new Set()
-function traverse (val, seen) {
+function traverse (val: any, seen?: Set) {
   let i, keys
   if (!seen) {
     seen = seenObjects
     seen.clear()
   }
-  const isA = isArray(val)
+  const isA = Array.isArray(val)
   const isO = isObject(val)
   if (isA || isO) {
     if (val.__ob__) {

+ 3 - 4
src/core/util/options.js

@@ -5,7 +5,6 @@ import { set } from '../observer/index'
 import {
   extend,
   isObject,
-  isArray,
   isPlainObject,
   hasOwn,
   camelize,
@@ -124,7 +123,7 @@ function mergeHook (parentVal, childVal) {
   return childVal
     ? parentVal
       ? parentVal.concat(childVal)
-      : isArray(childVal)
+      : Array.isArray(childVal)
         ? childVal
         : [childVal]
     : parentVal
@@ -166,7 +165,7 @@ strats.watch = function (parentVal, childVal) {
   for (const key in childVal) {
     let parent = ret[key]
     const child = childVal[key]
-    if (parent && !isArray(parent)) {
+    if (parent && !Array.isArray(parent)) {
       parent = [parent]
     }
     ret[key] = parent
@@ -236,7 +235,7 @@ function guardProps (options) {
   if (!props) return
   const res = {}
   let i, val, name
-  if (isArray(props)) {
+  if (Array.isArray(props)) {
     i = props.length
     while (i--) {
       val = props[i]

+ 3 - 3
src/core/util/props.js

@@ -1,4 +1,4 @@
-import { hasOwn, isArray, isObject, isPlainObject } from 'shared/util'
+import { hasOwn, isObject, isPlainObject } from 'shared/util'
 import { observe, observerState } from '../observer/index'
 import { warn } from './debug'
 
@@ -77,7 +77,7 @@ function assertProp (prop, name, value, vm, absent) {
   let valid = !type
   const expectedTypes = []
   if (type) {
-    if (!isArray(type)) {
+    if (!Array.isArray(type)) {
       type = [type]
     }
     for (let i = 0; i < type.length && !valid; i++) {
@@ -137,7 +137,7 @@ function assertType (value, type) {
     valid = isPlainObject(value)
   } else if (type === Array) {
     expectedType = 'array'
-    valid = isArray(value)
+    valid = Array.isArray(value)
   } else {
     valid = value instanceof type
   }

+ 5 - 5
src/core/vdom/helpers.js

@@ -1,4 +1,4 @@
-import { isArray, isPrimitive } from '../util/index'
+import { isPrimitive } from '../util/index'
 import VNode from './vnode'
 
 const whitespace = new VNode(undefined, undefined, undefined, ' ')
@@ -7,12 +7,12 @@ export function flatten (children) {
   if (typeof children === 'string') {
     return [new VNode(undefined, undefined, undefined, children)]
   }
-  if (isArray(children)) {
+  if (Array.isArray(children)) {
     const res = []
     for (let i = 0, l = children.length; i < l; i++) {
       const c = children[i]
       // flatten nested
-      if (isArray(c)) {
+      if (Array.isArray(c)) {
         res.push.apply(res, flatten(c))
       } else if (isPrimitive(c)) {
         // optimize whitespace
@@ -38,14 +38,14 @@ export function updateListeners (on, oldOn, add) {
     if (old === undefined) {
       capture = name.charAt(0) === '!'
       event = capture ? name.slice(1) : name
-      if (isArray(cur)) {
+      if (Array.isArray(cur)) {
         add(event, arrInvoker(cur), capture)
       } else {
         cur = { fn: cur }
         on[name] = cur
         add(event, fnInvoker(cur), capture)
       }
-    } else if (isArray(old)) {
+    } else if (Array.isArray(old)) {
       old.length = cur.length
       for (let i = 0; i < old.length; i++) old[i] = cur[i]
       on[name] = old

+ 2 - 2
src/platforms/web/runtime/modules/style.js

@@ -1,4 +1,4 @@
-import { extend, isArray, cached, camelize } from 'shared/util'
+import { extend, cached, camelize } from 'shared/util'
 import { inBrowser } from 'core/util/env'
 
 const prefixes = ['Webkit', 'Moz', 'ms']
@@ -28,7 +28,7 @@ function updateStyle (oldVnode, vnode) {
   let style = vnode.data.style || {}
 
   // handle array syntax
-  if (isArray(style)) {
+  if (Array.isArray(style)) {
     style = vnode.data.style = toObject(style)
   }
 

+ 2 - 2
src/platforms/web/util/class.js

@@ -1,4 +1,4 @@
-import { extend, isArray, isObject } from 'shared/util'
+import { extend, isObject } from 'shared/util'
 
 export function genClassForVnode (vnode) {
   let data = vnode.data
@@ -40,7 +40,7 @@ export function stringifyClass (value) {
   if (typeof value === 'string') {
     return value
   }
-  if (isArray(value)) {
+  if (Array.isArray(value)) {
     let res = ''
     for (let i = 0, l = value.length; i < l; i++) {
       if (value[i]) res += stringifyClass(value[i]) + ' '

+ 0 - 5
src/shared/util.js

@@ -146,11 +146,6 @@ export function isPlainObject (obj: any): boolean {
   return toString.call(obj) === OBJECT_STRING
 }
 
-/**
- * Array type check.
- */
-export const isArray = Array.isArray
-
 /**
  * Perform no operation.
  */