annotate observer

This commit is contained in:
Evan You 2016-05-12 20:36:16 -04:00
parent 1a65dc880f
commit a953bdeeb6
15 changed files with 110 additions and 136 deletions

View File

@ -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;`

View File

@ -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]

View File

@ -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)

View File

@ -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])
}

View File

@ -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

View File

@ -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 () {

View File

@ -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]
}

View File

@ -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) {

View File

@ -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__) {

View File

@ -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]

View File

@ -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
}

View File

@ -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

View File

@ -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)
}

View File

@ -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]) + ' '

View File

@ -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.
*/