diff --git a/src/compiler/events.js b/src/compiler/events.js index 2c00b63a..9bd80702 100644 --- a/src/compiler/events.js +++ b/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;` diff --git a/src/compiler/helpers.js b/src/compiler/helpers.js index bc7657fb..7e4178d6 100644 --- a/src/compiler/helpers.js +++ b/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] diff --git a/src/core/instance/render.js b/src/core/instance/render.js index 17b341bd..4ef11b7e 100644 --- a/src/core/instance/render.js +++ b/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) { render: () => VNode ): ?Array { let ret: ?Array, 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) diff --git a/src/core/instance/state.js b/src/core/instance/state.js index 5c9d3e67..e6b528b3 100644 --- a/src/core/instance/state.js +++ b/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]) } diff --git a/src/core/observer/array.js b/src/core/observer/array.js index 3c071334..37c7bd11 100644 --- a/src/core/observer/array.js +++ b/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 diff --git a/src/core/observer/dep.js b/src/core/observer/dep.js index 9d6468e5..058d6a8a 100644 --- a/src/core/observer/dep.js +++ b/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; + 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 () { diff --git a/src/core/observer/index.js b/src/core/observer/index.js index c729698d..b537b40c 100644 --- a/src/core/observer/index.js +++ b/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; + + 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) { 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 | 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] } diff --git a/src/core/observer/scheduler.js b/src/core/observer/scheduler.js index 02d7f5d0..fdc53461 100644 --- a/src/core/observer/scheduler.js +++ b/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 = [] +const userQueue: Array = [] +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) { // 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) { diff --git a/src/core/observer/watcher.js b/src/core/observer/watcher.js index 376b0945..7b19394f 100644 --- a/src/core/observer/watcher.js +++ b/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; + newDeps: Array; + 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__) { diff --git a/src/core/util/options.js b/src/core/util/options.js index 59bee52d..79ad329e 100644 --- a/src/core/util/options.js +++ b/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] diff --git a/src/core/util/props.js b/src/core/util/props.js index e49735af..ddb3b731 100644 --- a/src/core/util/props.js +++ b/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 } diff --git a/src/core/vdom/helpers.js b/src/core/vdom/helpers.js index c29b2fbf..0ad72ddd 100644 --- a/src/core/vdom/helpers.js +++ b/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 diff --git a/src/platforms/web/runtime/modules/style.js b/src/platforms/web/runtime/modules/style.js index 86d43527..5e561ccd 100644 --- a/src/platforms/web/runtime/modules/style.js +++ b/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) } diff --git a/src/platforms/web/util/class.js b/src/platforms/web/util/class.js index d8cdb0f0..5193fae8 100644 --- a/src/platforms/web/util/class.js +++ b/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]) + ' ' diff --git a/src/shared/util.js b/src/shared/util.js index 3b7d9627..b97e3738 100644 --- a/src/shared/util.js +++ b/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. */