소스 검색

rework type annotation strategy: use interface instead

Evan You 10 년 전
부모
커밋
1b7ab89a5d

+ 1 - 0
.flowconfig

@@ -8,6 +8,7 @@
 [include]
 
 [libs]
+flow
 
 [options]
 module.name_mapper='^compiler/\(.*\)$' -> '<PROJECT_ROOT>/src/compiler/\1'

+ 116 - 0
flow/declarations.js

@@ -0,0 +1,116 @@
+import type { Config } from '../src/core/config'
+import type VNode from '../src/core/vdom/vnode'
+import type Watcher from '../src/core/observer/watcher'
+
+declare interface Component {
+  // constructor information
+  static cid: number;
+  static options: Object;
+  // extend
+  static extend: (options: Object) => Function;
+  // assets
+  static directive: (id: string, def?: Function | Object) => Function | Object | void;
+  static component: (id: string, def?: Class<Component> | Object) => Class<Component>;
+  static transition: (id: string, def?: Object) => Object | void;
+  static filter: (id: string, def?: Function) => Function | void;
+
+  // public properties
+  $el: Element | void;
+  $data: Object;
+  $options: Object;
+  $parent: Component | void;
+  $root: Component;
+  $children: Array<Component>;
+  $refs: { [key: string]: Component | Element | Array<Component | Element> | void };
+  $slots: { [key: string]: Array<VNode> };
+  $isServer: boolean;
+
+  // public methods
+  $mount: (el?: Element | string) => Component;
+  $forceUpdate: () => void;
+  $destroy: () => void;
+  $watch: (expOrFn: string | Function, cb: Function, options?: Object) => Function;
+  $on: (event: string, fn: Function) => Component;
+  $once: (event: string, fn: Function) => Component;
+  $off: (event?: string, fn?: Function) => Component;
+  $emit: (event: string, ...args: Array<any>) => Component;
+  $nextTick: (fn: Function) => void;
+  $createElement: (
+    tag?: string | Component,
+    data?: Object,
+    children?: Array<?VNode> | string,
+    namespace?: string
+  ) => VNode;
+
+  // private properties
+  _uid: number;
+  _isVue: true;
+  _renderProxy: Component;
+  _watcher: Watcher;
+  _watchers: Array<Watcher>;
+  _data: Object;
+  _events: Object;
+  _isMounted: boolean;
+  _isDestroyed: boolean;
+  _isBeingDestroyed: boolean;
+  _vnode: ?VNode;
+  _staticTrees: ?Array<VNode>;
+
+  // private methods
+  // lifecycle
+  _mount: () => Component;
+  _update: (vnode: VNode) => void;
+  _updateFromParent: (
+    propsData?: Object,
+    listeners?: { [key: string]: Function | Array<Function> },
+    parentVnode: VNode,
+    renderChildren: Array<VNode> | () => Array<VNode>
+  ) => void;
+  // rendering
+  _render: () => VNode;
+  __patch__: (a: Element | VNode | void, b: VNode) => Element;
+  __h__: (
+    tag?: string | Component | Object,
+    data?: Object,
+    children?: Array<?VNode> | string,
+    namespace?: string
+  ) => VNode;
+  __toString__: (value: any) => string;
+  __resolveFilter__: (id: string) => Function;
+  __renderList__: (
+    val: any,
+    render: Function
+  ) => ?Array<VNode>;
+  __registerRef__: (
+    key: string,
+    ref: Component | Element,
+    vFor: boolean,
+    isRemoval: boolean
+  ) => void;
+
+  // allow dynamic method registration
+  [key: string]: any
+}
+
+declare interface GlobalAPI {
+  cid: number;
+  options: Object;
+  config: Config;
+  util: Object;
+
+  extend: (options: Object) => Function;
+  set: (obj: Object, key: string, value: any) => void;
+  delete: (obj: Object, key: string) => void;
+  nextTick: (fn: Function, context?: Object) => void;
+  use: (plugin: Function | Object) => void;
+  mixin: (mixin: Object) => void;
+  compile: (template: string) => { render: Function, staticRenderFns: Array<Function> };
+
+  directive: (id: string, def?: Function | Object) => Function | Object | void;
+  component: (id: string, def?: Class<Component> | Object) => Class<Component>;
+  transition: (id: string, def?: Object) => Object | void;
+  filter: (id: string, def?: Function) => Function | void;
+
+  // allow dynamic method registration
+  [key: string]: any
+}

+ 6 - 6
src/core/global-api/assets.js

@@ -1,17 +1,17 @@
-/*
- * not type checking this file because flow doesn't like dynamically setting
- * fields on a class
- */
+/* @flow */
 
 import config from '../config'
 import { warn, isPlainObject } from '../util/index'
 
-export function initAssetRegisters (Vue) {
+export function initAssetRegisters (Vue: GlobalAPI) {
   /**
    * Create asset registration methods.
    */
   config._assetTypes.forEach(type => {
-    Vue[type] = function (id, definition) {
+    Vue[type] = function (
+      id: string,
+      definition: Function | Object
+    ): Function | Object | void {
       if (!definition) {
         return this.options[type + 's'][id]
       } else {

+ 4 - 4
src/core/global-api/extend.js

@@ -1,10 +1,10 @@
 /* @flow */
 
-import type Vue from '../instance/index'
 import config from '../config'
+import { init } from '../instance/init'
 import { warn, mergeOptions } from '../util/index'
 
-export function initExtend (Vue: Class<Vue>) {
+export function initExtend (Vue: GlobalAPI) {
   /**
    * Each instance constructor, including Vue, has a unique
    * cid. This enables us to create wrapped "child
@@ -16,7 +16,7 @@ export function initExtend (Vue: Class<Vue>) {
   /**
    * Class inheritance
    */
-  Vue.extend = function (extendOptions: Object): Class<any> {
+  Vue.extend = function (extendOptions: Object): Function {
     extendOptions = extendOptions || {}
     const Super = this
     const isFirstExtend = Super.cid === 0
@@ -34,7 +34,7 @@ export function initExtend (Vue: Class<Vue>) {
       }
     }
     const Sub = function VueComponent (options) {
-      this._init(options)
+      init(this, options)
     }
     Sub.prototype = Object.create(Super.prototype)
     Sub.prototype.constructor = Sub

+ 1 - 2
src/core/global-api/index.js

@@ -1,6 +1,5 @@
 /* @flow */
 
-import type Vue from '../instance/index'
 import config from '../config'
 import * as util from '../util/index'
 import { initUse } from './use'
@@ -9,7 +8,7 @@ import { initExtend } from './extend'
 import { initAssetRegisters } from './assets'
 import { set, del } from '../observer/index'
 
-export function initGlobalAPI (Vue: Class<Vue>) {
+export function initGlobalAPI (Vue: GlobalAPI) {
   Vue.config = config
   Vue.util = util
   Vue.set = set

+ 1 - 2
src/core/global-api/mixin.js

@@ -1,9 +1,8 @@
 /* @flow */
 
-import type Vue from '../instance/index'
 import { mergeOptions } from '../util/index'
 
-export function initMixin (Vue: Class<Vue>) {
+export function initMixin (Vue: GlobalAPI) {
   Vue.mixin = function (mixin: Object) {
     Vue.options = mergeOptions(Vue.options, mixin)
   }

+ 1 - 2
src/core/global-api/use.js

@@ -1,9 +1,8 @@
 /* @flow */
 
-import type Vue from '../instance/index'
 import { toArray } from '../util/index'
 
-export function initUse (Vue: Class<Vue>) {
+export function initUse (Vue: GlobalAPI) {
   Vue.use = function (plugin: Function | Object) {
     /* istanbul ignore if */
     if (plugin.installed) {

+ 1 - 3
src/core/index.js

@@ -1,11 +1,9 @@
-import Vue from './instance/index'
 import config from './config'
 import { initGlobalAPI } from './global-api/index'
+import Vue from './instance/index'
 
 initGlobalAPI(Vue)
 
-// defining $isServer flag here because flow cannot handle
-// Object.defineProperty getters
 Object.defineProperty(Vue.prototype, '$isServer', {
   get: () => config._isServer
 })

+ 26 - 24
src/core/instance/events.js

@@ -1,10 +1,9 @@
 /* @flow */
 
-import type Vue from './index'
 import { toArray } from '../util/index'
 import { updateListeners } from '../vdom/helpers'
 
-export function initEvents (vm: Vue) {
+export function initEvents (vm: Component) {
   vm._events = Object.create(null)
   // init parent attached events
   const listeners = vm.$options._parentListeners
@@ -15,37 +14,39 @@ export function initEvents (vm: Vue) {
   }
 }
 
-export function eventsMixin (Vue: Class<Vue>) {
-  Vue.prototype.$on = function (event: string, fn: Function): Vue {
-    (this._events[event] || (this._events[event] = [])).push(fn)
-    return this
+export function eventsMixin (Vue: Class<Component>) {
+  Vue.prototype.$on = function (event: string, fn: Function): Component {
+    const vm: Component = this
+    ;(vm._events[event] || (vm._events[event] = [])).push(fn)
+    return vm
   }
 
-  Vue.prototype.$once = function (event: string, fn: Function): Vue {
-    const self = this
+  Vue.prototype.$once = function (event: string, fn: Function): Component {
+    const vm: Component = this
     function on () {
-      self.$off(event, on)
-      fn.apply(this, arguments)
+      vm.$off(event, on)
+      fn.apply(vm, arguments)
     }
     on.fn = fn
-    this.$on(event, on)
-    return this
+    vm.$on(event, on)
+    return vm
   }
 
-  Vue.prototype.$off = function (event?: string, fn?: Function): Vue {
+  Vue.prototype.$off = function (event?: string, fn?: Function): Component {
+    const vm: Component = this
     // all
     if (!arguments.length) {
-      this._events = Object.create(null)
-      return this
+      vm._events = Object.create(null)
+      return vm
     }
     // specific event
-    const cbs = this._events[event]
+    const cbs = vm._events[event]
     if (!cbs) {
-      return this
+      return vm
     }
     if (arguments.length === 1) {
-      this._events[event] = null
-      return this
+      vm._events[event] = null
+      return vm
     }
     // specific handler
     let cb
@@ -57,18 +58,19 @@ export function eventsMixin (Vue: Class<Vue>) {
         break
       }
     }
-    return this
+    return vm
   }
 
-  Vue.prototype.$emit = function (event: string): Vue {
-    let cbs = this._events[event]
+  Vue.prototype.$emit = function (event: string): Component {
+    const vm: Component = this
+    let cbs = vm._events[event]
     if (cbs) {
       cbs = cbs.length > 1 ? toArray(cbs) : cbs
       const args = toArray(arguments, 1)
       for (let i = 0, l = cbs.length; i < l; i++) {
-        cbs[i].apply(this, args)
+        cbs[i].apply(vm, args)
       }
     }
-    return this
+    return vm
   }
 }

+ 10 - 131
src/core/instance/index.js

@@ -1,137 +1,16 @@
-/* @flow */
-
-import type { Config } from '../config'
-import type VNode from '../vdom/vnode'
-import type Watcher from '../observer/watcher'
-
-import { initProxy } from './proxy'
-import { initState, stateMixin } from './state'
-import { initRender, renderMixin } from './render'
-import { initEvents, eventsMixin } from './events'
-import { initLifecycle, lifecycleMixin, callHook } from './lifecycle'
-import { mergeOptions } from '../util/index'
-
-let uid = 0
-
-export default class Vue {
-  // static properties
-  static cid: number;
-  static options: Object;
-  static config: Config;
-  static util: Object;
-
-  // static methods
-  static set: (obj: Object, key: string, value: any) => void;
-  static delete: (obj: Object, key: string) => void;
-  static nextTick: (fn: Function, context?: Object) => void;
-  static use: (plugin: Function | Object) => void;
-  static mixin: (mixin: Object) => void;
-  static extend: (options: Object) => Class<any>;
-  static compile: (template: string) => { render: Function, staticRenderFns: Array<Function> };
-
-  // assets
-  static directive: (id: string, def?: Function | Object) => Function | Object | void;
-  static component: (id: string, def?: Class<any> | Object) => Class<any>;
-  static transition: (id: string, def?: Object) => Object | void;
-  static filter: (id: string, def?: Function) => Function | void;
-
-  // public properties
-  $el: Element | void;
-  $data: Object;
-  $options: Object;
-  $parent: Vue | void;
-  $root: Vue;
-  $children: Array<Vue>;
-  $refs: { [key: string]: Vue | Element };
-  $slots: { [key: string]: Array<VNode> };
-  $isServer: boolean;
-
-  // public methods
-  $mount: (el?: Element | string) => Vue;
-  $forceUpdate: () => void;
-  $destroy: () => void;
-  $watch: (expOrFn: string | Function, cb: Function, options?: Object) => Function;
-  $on: (event: string, fn: Function) => Vue;
-  $once: (event: string, fn: Function) => Vue;
-  $off: (event?: string, fn?: Function) => Vue;
-  $emit: (event: string, ...args: Array<any>) => Vue;
-  $nextTick: (fn: Function) => void;
-  $createElement: (
-    tag?: string | Vue,
-    data?: Object,
-    children?: Array<?VNode> | string,
-    namespace?: string
-  ) => VNode;
-
-  // private properties
-  _uid: number;
-  _isVue: true;
-  _renderProxy: Vue;
-  _watchers: Array<Watcher>;
-  _data: Object;
-  _events: { [key: string]: Array<Function> };
-  _isMounted: boolean;
-  _isDestroyed: boolean;
-  _isBeingDestroyed: boolean;
-  _vnode: ?VNode;
-  _staticTrees: ?Array<VNode>;
-
-  // private methods
-  // lifecycle
-  _mount: () => Vue;
-  _update: (vnode: VNode) => void;
-  _updateFromParent: (propsData?: Object, listeners?: Object, parentVnode: VNode, renderChildren: Array<VNode> | () => Array<VNode>) => void;
-  // rendering
-  _render: () => VNode;
-  __h__: (
-    tag?: string | Vue,
-    data?: Object,
-    children?: Array<?VNode> | string,
-    namespace?: string
-  ) => VNode;
-  __toString__: (value: any) => string;
-  __resolveFilter__: (id: string) => Function;
-  __renderList__: (
-    val: any,
-    render: Function
-  ) => ?Array<VNode>;
-  __registerRef__: (
-    key: string,
-    ref: Vue | Element,
-    vFor: boolean,
-    isRemoval: boolean
-  ) => void;
-
-  constructor (options?: Object) {
-    this._init(options)
-  }
-
-  _init (options?: Object) {
-    // a uid
-    this._uid = uid++
-    // a flag to avoid this being observed
-    this._isVue = true
-    // merge options
-    this.$options = mergeOptions(
-      this.constructor.options,
-      options || {},
-      this
-    )
-    if (process.env.NODE_ENV !== 'production') {
-      initProxy(this)
-    } else {
-      this._renderProxy = this
-    }
-    initLifecycle(this)
-    initEvents(this)
-    callHook(this, 'init')
-    initState(this)
-    callHook(this, 'created')
-    initRender(this)
-  }
+import { init } from './init'
+import { stateMixin } from './state'
+import { renderMixin } from './render'
+import { eventsMixin } from './events'
+import { lifecycleMixin } from './lifecycle'
+
+function Vue (options) {
+  init(this, options)
 }
 
 stateMixin(Vue)
 eventsMixin(Vue)
 lifecycleMixin(Vue)
 renderMixin(Vue)
+
+export default Vue

+ 34 - 0
src/core/instance/init.js

@@ -0,0 +1,34 @@
+/* @flow */
+
+import { initProxy } from './proxy'
+import { initState } from './state'
+import { initRender } from './render'
+import { initEvents } from './events'
+import { initLifecycle, callHook } from './lifecycle'
+import { mergeOptions } from '../util/index'
+
+let uid = 0
+
+export function init (vm: Component, options?: Object) {
+  // a uid
+  vm._uid = uid++
+  // a flag to avoid this being observed
+  vm._isVue = true
+  // merge options
+  vm.$options = mergeOptions(
+    vm.constructor.options,
+    options || {},
+    vm
+  )
+  if (process.env.NODE_ENV !== 'production') {
+    initProxy(vm)
+  } else {
+    vm._renderProxy = vm
+  }
+  initLifecycle(vm)
+  initEvents(vm)
+  callHook(vm, 'init')
+  initState(vm)
+  callHook(vm, 'created')
+  initRender(vm)
+}

+ 52 - 52
src/core/instance/lifecycle.js

@@ -1,13 +1,12 @@
 /* @flow */
 
-import type Vue from './index'
 import type VNode from '../vdom/vnode'
 import Watcher from '../observer/watcher'
 import { warn, validateProp, remove } from '../util/index'
 import { observerState } from '../observer/index'
 import { updateListeners } from '../vdom/helpers'
 
-export function initLifecycle (vm: Vue) {
+export function initLifecycle (vm: Component) {
   const options = vm.$options
 
   vm.$parent = options.parent
@@ -24,56 +23,58 @@ export function initLifecycle (vm: Vue) {
   vm._isBeingDestroyed = false
 }
 
-export function lifecycleMixin (Vue: Class<Vue>) {
-  Vue.prototype._mount = function (): Vue {
-    if (!this.$options.render) {
-      this.$options.render = () => this.$createElement('div')
+export function lifecycleMixin (Vue: Class<Component>) {
+  Vue.prototype._mount = function (): Component {
+    const vm: Component = this
+    if (!vm.$options.render) {
+      vm.$options.render = () => vm.$createElement('div')
       if (process.env.NODE_ENV !== 'production') {
-        if (this.$options.template) {
+        if (vm.$options.template) {
           warn(
             'You are using the runtime-only build of Vue where the template ' +
             'option is not available. Either pre-compile the templates into ' +
             'render functions, or use the compiler-included build.',
-            this
+            vm
           )
         } else {
           warn(
             'Failed to mount component: template or render function not defined.',
-            this
+            vm
           )
         }
       }
     }
-    callHook(this, 'beforeMount')
-    this._watcher = new Watcher(this, this._render, this._update)
-    this._update(this._watcher.value)
-    this._isMounted = true
+    callHook(vm, 'beforeMount')
+    vm._watcher = new Watcher(vm, vm._render, vm._update)
+    vm._update(vm._watcher.value)
+    vm._isMounted = true
     // root instance, call mounted on self
-    if (this.$root === this) {
-      callHook(this, 'mounted')
+    if (vm.$root === vm) {
+      callHook(vm, 'mounted')
     }
-    return this
+    return vm
   }
 
   Vue.prototype._update = function (vnode: VNode) {
-    if (this._isMounted) {
-      callHook(this, 'beforeUpdate')
+    const vm: Component = this
+    if (vm._isMounted) {
+      callHook(vm, 'beforeUpdate')
     }
-    if (!this._vnode) {
+    if (!vm._vnode) {
       // Vue.prototype.__patch__ is injected in entry points
       // based on the rendering backend used.
-      this.$el = this.__patch__(this.$el, vnode)
+      vm.$el = vm.__patch__(vm.$el, vnode)
     } else {
-      this.$el = this.__patch__(this._vnode, vnode)
+      vm.$el = vm.__patch__(vm._vnode, vnode)
     }
-    this._vnode = vnode
+    vm._vnode = vnode
     // update parent vnode element after patch
-    const parentNode = this.$options._parentVnode
+    const parentNode = vm.$options._parentVnode
     if (parentNode) {
-      parentNode.elm = this.$el
+      parentNode.elm = vm.$el
     }
-    if (this._isMounted) {
-      callHook(this, 'updated')
+    if (vm._isMounted) {
+      callHook(vm, 'updated')
     }
   }
 
@@ -83,66 +84,65 @@ export function lifecycleMixin (Vue: Class<Vue>) {
     parentVnode: VNode,
     renderChildren: Array<VNode> | () => Array<VNode>
   ) {
-    this.$options._parentVnode = parentVnode
-    this.$options._renderChildren = renderChildren
+    const vm: Component = this
+    vm.$options._parentVnode = parentVnode
+    vm.$options._renderChildren = renderChildren
     // update props
-    if (propsData && this.$options.props) {
+    if (propsData && vm.$options.props) {
       observerState.shouldConvert = false
-      const propKeys = this.$options.propKeys
+      const propKeys = vm.$options.propKeys
       for (let i = 0; i < propKeys.length; i++) {
         const key = propKeys[i]
-        this[key] = validateProp(this, key, propsData)
+        vm[key] = validateProp(vm, key, propsData)
       }
       observerState.shouldConvert = true
     }
     // update listeners
     if (listeners) {
-      const oldListeners = this.$options._parentListeners
-      this.$options._parentListeners = listeners
+      const oldListeners = vm.$options._parentListeners
+      vm.$options._parentListeners = listeners
       updateListeners(listeners, oldListeners || {}, (event, handler) => {
-        this.$on(event, handler)
+        vm.$on(event, handler)
       })
     }
   }
 
   Vue.prototype.$forceUpdate = function () {
-    this._watcher.update()
+    const vm: Component = this
+    vm._watcher.update()
   }
 
   Vue.prototype.$destroy = function () {
-    if (this._isDestroyed) {
+    const vm: Component = this
+    if (vm._isDestroyed) {
       return
     }
-    callHook(this, 'beforeDestroy')
-    this._isBeingDestroyed = true
+    callHook(vm, 'beforeDestroy')
+    vm._isBeingDestroyed = true
     // remove self from parent
-    const parent = this.$parent
+    const parent = vm.$parent
     if (parent && !parent._isBeingDestroyed) {
-      remove(parent.$children, this)
-    }
-    // unregister ref
-    if (this._ref) {
-      this._context.$refs[this._ref] = undefined
+      remove(parent.$children, vm)
     }
     // teardown watchers
-    let i = this._watchers.length
+    let i = vm._watchers.length
     while (i--) {
-      this._watchers[i].teardown()
+      vm._watchers[i].teardown()
     }
     // remove reference from data ob
     // frozen object may not have observer.
-    if (this._data.__ob__) {
-      this._data.__ob__.removeVm(this)
+    if (vm._data.__ob__) {
+      vm._data.__ob__.removeVm(vm)
     }
     // call the last hook...
-    this._isDestroyed = true
-    callHook(this, 'destroyed')
+    vm._isDestroyed = true
+    callHook(vm, 'destroyed')
     // turn off all instance listeners.
-    this.$off()
+    vm.$off()
   }
 }
 
-export function callHook (vm: Vue, hook: string) {
+export function callHook (vm: Component, hook: string) {
   vm.$emit('pre-hook:' + hook)
   const handlers = vm.$options[hook]
   if (handlers) {

+ 19 - 16
src/core/instance/render.js

@@ -1,6 +1,5 @@
 /* @flow */
 
-import type Vue from './index'
 import type VNode from '../vdom/vnode'
 import createElement from '../vdom/create-element'
 import { emptyVNode } from '../vdom/vnode'
@@ -8,11 +7,13 @@ import { flatten } from '../vdom/helpers'
 import { bind, remove, isObject, renderString } from 'shared/util'
 import { resolveAsset, nextTick } from '../util/index'
 
-export const renderState = {
+export const renderState: {
+  activeInstance: Component | null
+} = {
   activeInstance: null
 }
 
-export function initRender (vm: Vue) {
+export function initRender (vm: Component) {
   vm._vnode = null
   vm._staticTrees = null
   vm.$slots = {}
@@ -24,26 +25,27 @@ export function initRender (vm: Vue) {
   }
 }
 
-export function renderMixin (Vue: Class<Vue>) {
-  Vue.prototype.$nextTick = function (fn) {
+export function renderMixin (Vue: Class<Component>) {
+  Vue.prototype.$nextTick = function (fn: Function) {
     nextTick(fn, this)
   }
 
   Vue.prototype._render = function (): VNode {
-    if (!this._isMounted) {
+    const vm: Component = this
+    if (!vm._isMounted) {
       // render static sub-trees for once on initial render
-      renderStaticTrees(this)
+      renderStaticTrees(vm)
     }
     const prev = renderState.activeInstance
-    renderState.activeInstance = this
-    const { render, _renderChildren, _parentVnode } = this.$options
+    renderState.activeInstance = vm
+    const { render, _renderChildren, _parentVnode } = vm.$options
     // resolve slots. becaues slots are rendered in parent scope,
     // we set the activeInstance to parent.
     if (_renderChildren) {
-      resolveSlots(this, _renderChildren)
+      resolveSlots(vm, _renderChildren)
     }
     // render self
-    const vnode = render.call(this._renderProxy) || emptyVNode
+    const vnode = render.call(vm._renderProxy) || emptyVNode
     // set parent
     vnode.parent = _parentVnode
     // restore render state
@@ -97,16 +99,17 @@ export function renderMixin (Vue: Class<Vue>) {
     vFor: boolean,
     isRemoval: boolean
   ) {
-    const refs = this.$refs
+    const vm: Component = this
+    const refs = vm.$refs
     if (isRemoval) {
-      if (vFor) {
+      if (Array.isArray(refs[key])) {
         remove(refs[key], ref)
       } else {
         refs[key] = undefined
       }
     } else {
       if (vFor) {
-        if (refs[key]) {
+        if (Array.isArray(refs[key])) {
           refs[key].push(ref)
         } else {
           refs[key] = [ref]
@@ -118,7 +121,7 @@ export function renderMixin (Vue: Class<Vue>) {
   }
 }
 
-function renderStaticTrees (vm: Vue) {
+function renderStaticTrees (vm: Component) {
   const staticRenderFns = vm.$options.staticRenderFns
   if (staticRenderFns) {
     const trees = vm._staticTrees = new Array(staticRenderFns.length)
@@ -128,7 +131,7 @@ function renderStaticTrees (vm: Vue) {
   }
 }
 
-function resolveSlots (vm: Vue, renderChildren: () => Array<?VNode> | void) {
+function resolveSlots (vm: Component, renderChildren: () => Array<?VNode> | void) {
   if (renderChildren) {
     const children = flatten(renderChildren())
     const slots = {}

+ 44 - 33
src/core/instance/state.js

@@ -1,4 +1,4 @@
-/* Not type checking this file because flow doesn't play well with Object.defineProperty */
+/* @flow */
 
 import Watcher from '../observer/watcher'
 import Dep from '../observer/dep'
@@ -18,7 +18,7 @@ import {
   noop
 } from '../util/index'
 
-export function initState (vm) {
+export function initState (vm: Component) {
   vm._watchers = []
   initProps(vm)
   initData(vm)
@@ -27,7 +27,7 @@ export function initState (vm) {
   initWatch(vm)
 }
 
-function initProps (vm) {
+function initProps (vm: Component) {
   const props = vm.$options.props
   const propsData = vm.$options.propsData
   if (props) {
@@ -43,7 +43,7 @@ function initProps (vm) {
   }
 }
 
-function initData (vm) {
+function initData (vm: Component) {
   let data = vm.$options.data
   data = vm._data = typeof data === 'function'
     ? data()
@@ -65,35 +65,38 @@ function initData (vm) {
   observe(data, vm)
 }
 
-function initComputed (vm) {
+const computedSharedDefinition = {
+  enumerable: true,
+  configurable: true,
+  get: noop,
+  set: noop
+}
+
+function initComputed (vm: Component) {
   const computed = vm.$options.computed
   if (computed) {
     for (const key in computed) {
       const userDef = computed[key]
-      const def = {
-        enumerable: true,
-        configurable: true
-      }
       if (typeof userDef === 'function') {
-        def.get = makeComputedGetter(userDef, vm)
-        def.set = noop
+        computedSharedDefinition.get = makeComputedGetter(userDef, vm)
+        computedSharedDefinition.set = noop
       } else {
-        def.get = userDef.get
+        computedSharedDefinition.get = userDef.get
           ? userDef.cache !== false
             ? makeComputedGetter(userDef.get, vm)
             : bind(userDef.get, vm)
           : noop
-        def.set = userDef.set
+        computedSharedDefinition.set = userDef.set
           ? bind(userDef.set, vm)
           : noop
       }
-      Object.defineProperty(vm, key, def)
+      Object.defineProperty(vm, key, computedSharedDefinition)
     }
   }
 }
 
-function makeComputedGetter (getter, owner) {
-  const watcher = new Watcher(owner, getter, null, {
+function makeComputedGetter (getter: Function, owner: Component): Function {
+  const watcher = new Watcher(owner, getter, noop, {
     lazy: true
   })
   return function computedGetter () {
@@ -107,7 +110,7 @@ function makeComputedGetter (getter, owner) {
   }
 }
 
-function initMethods (vm) {
+function initMethods (vm: Component) {
   const methods = vm.$options.methods
   if (methods) {
     for (const key in methods) {
@@ -116,7 +119,7 @@ function initMethods (vm) {
   }
 }
 
-function initWatch (vm) {
+function initWatch (vm: Component) {
   const watch = vm.$options.watch
   if (watch) {
     for (const key in watch) {
@@ -132,7 +135,7 @@ function initWatch (vm) {
   }
 }
 
-function createWatcher (vm, key, handler) {
+function createWatcher (vm: Component, key: string, handler: any) {
   let options
   if (isPlainObject(handler)) {
     options = handler
@@ -144,24 +147,32 @@ function createWatcher (vm, key, handler) {
   vm.$watch(key, handler, options)
 }
 
-export function stateMixin (Vue) {
-  Object.defineProperty(Vue.prototype, '$data', {
-    get () {
-      return this._data
-    },
-    set (newData) {
-      if (newData !== this._data) {
-        setData(this, newData)
-      }
+export function stateMixin (Vue: Class<Component>) {
+  // flow somehow has problems with directly declared definition object
+  // when using Object.defineProperty, so we have to procedurally build up
+  // the object here.
+  const dataDef = {}
+  dataDef.get = function () {
+    return this._data
+  }
+  dataDef.set = function (newData: Object) {
+    if (newData !== this._data) {
+      setData(this, newData)
     }
-  })
+  }
+  Object.defineProperty(Vue.prototype, '$data', dataDef)
 
-  Vue.prototype.$watch = function (expOrFn, cb, options) {
+  Vue.prototype.$watch = function (
+    expOrFn: string | Function,
+    cb: Function,
+    options?: Object
+  ): Function {
+    const vm: Component = this
     options = options || {}
     options.user = true
-    const watcher = new Watcher(this, expOrFn, cb, options)
+    const watcher = new Watcher(vm, expOrFn, cb, options)
     if (options.immediate) {
-      cb.call(this, watcher.value)
+      cb.call(vm, watcher.value)
     }
     return function unwatchFn () {
       watcher.teardown()
@@ -169,7 +180,7 @@ export function stateMixin (Vue) {
   }
 }
 
-function setData (vm, newData) {
+function setData (vm: Component, newData: Object) {
   newData = newData || {}
   const oldData = vm._data
   vm._data = newData

+ 7 - 11
src/core/observer/index.js

@@ -1,6 +1,5 @@
 /* @flow */
 
-import type Vue from '../instance/index'
 import config from '../config'
 import Dep from './dep'
 import { arrayMethods } from './array'
@@ -36,7 +35,7 @@ export const observerState = {
 export class Observer {
   value: any;
   dep: Dep;
-  vms: ?Array<Vue>;
+  vms: ?Array<Component>;
 
   constructor (value: any) {
     this.value = value
@@ -81,7 +80,7 @@ export class Observer {
    * digest the watchers. This is only called when the object
    * is observed as an instance's root $data.
    */
-  addVm (vm: Vue) {
+  addVm (vm: Component) {
     (this.vms || (this.vms = [])).push(vm)
   }
 
@@ -89,7 +88,7 @@ export class Observer {
    * Remove an owner vm. This is called when the object is
    * swapped out as an instance's $data object.
    */
-  removeVm (vm: Vue) {
+  removeVm (vm: Component) {
     remove(this.vms, vm)
   }
 }
@@ -109,11 +108,8 @@ function protoAugment (target, src: Object) {
 /**
  * Augment an target Object or Array by defining
  * hidden properties.
- *
- * @param {Object|Array} target
- * @param {Object} proto
  */
-function copyAugment (target, src, keys) {
+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])
@@ -125,7 +121,7 @@ function copyAugment (target, src, keys) {
  * returns the new observer if successfully observed,
  * or the existing observer if the value already has one.
  */
-export function observe (value: any, vm?: Vue): Observer | void {
+export function observe (value: any, vm?: Component): Observer | void {
   if (!isObject(value)) {
     return
   }
@@ -266,7 +262,7 @@ export function del (obj: Object, key: string) {
   }
 }
 
-export function proxy (vm: Vue, key: string) {
+export function proxy (vm: Component, key: string) {
   if (!isReserved(key)) {
     Object.defineProperty(vm, key, {
       configurable: true,
@@ -282,7 +278,7 @@ export function proxy (vm: Vue, key: string) {
 }
 
 // using Object type to avoid flow complaining
-export function unproxy (vm: Object, key: string) {
+export function unproxy (vm: Component, key: string) {
   if (!isReserved(key)) {
     delete vm[key]
   }

+ 2 - 3
src/core/observer/watcher.js

@@ -1,6 +1,5 @@
 /* @flow */
 
-import type Vue from '../instance/index'
 import Dep from './dep'
 import { queueWatcher } from './scheduler'
 import {
@@ -20,7 +19,7 @@ let prevTarget
  * This is used for both the $watch() api and directives.
  */
 export default class Watcher {
-  vm: Vue;
+  vm: Component;
   expression: string;
   cb: Function;
   id: number;
@@ -37,7 +36,7 @@ export default class Watcher {
   value: any;
 
   constructor (
-    vm: Vue,
+    vm: Component,
     expOrFn: string | Function,
     cb: Function,
     options?: Object = {}

+ 2 - 2
src/core/util/options.js

@@ -69,7 +69,7 @@ function mergeData (to: Object, from: ?Object): Object {
 strats.data = function (
   parentVal: any,
   childVal: any,
-  vm?: Vue
+  vm?: Component
 ): ?Function {
   if (!vm) {
     // in a Vue.extend merge, both should be functions
@@ -276,7 +276,7 @@ function guardDirectives (options: Object) {
  * Merge two option objects into a new one.
  * Core utility used in both instantiation and inheritance.
  */
-export function mergeOptions (parent: Object, child: Object, vm?: Vue) {
+export function mergeOptions (parent: Object, child: Object, vm?: Component) {
   guardComponents(child)
   guardProps(child)
   guardDirectives(child)

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

@@ -1,6 +1,5 @@
 /* @flow */
 
-import type Vue from '../instance/index'
 import { hasOwn, isObject, isPlainObject } from 'shared/util'
 import { observe, observerState } from '../observer/index'
 import { warn } from './debug'
@@ -12,7 +11,7 @@ type PropOptions = {
   validator: ?Function
 }
 
-export function validateProp (vm: Vue, key: string, propsData: ?Object): any {
+export function validateProp (vm: Component, key: string, propsData: ?Object): any {
   if (!propsData) return
   const prop = vm.$options.props[key]
   const absent = hasOwn(propsData, key)
@@ -35,7 +34,7 @@ export function validateProp (vm: Vue, key: string, propsData: ?Object): any {
 /**
  * Get the default value of a prop.
  */
-function getPropDefaultValue (vm: Vue, prop: PropOptions, name: string): any {
+function getPropDefaultValue (vm: Component, prop: PropOptions, name: string): any {
   // no default, return undefined
   if (!hasOwn(prop, 'default')) {
     // absent boolean value defaults to false
@@ -66,7 +65,7 @@ function assertProp (
   prop: PropOptions,
   name: string,
   value: any,
-  vm: Vue,
+  vm: Component,
   absent: boolean
 ) {
   if (prop.required && absent) {

+ 1 - 1
src/entries/web-runtime-with-compiler.js

@@ -1,9 +1,9 @@
 /* @flow */
 
+import Vue from './web-runtime'
 import config from 'core/config'
 import { warn, cached } from 'core/util/index'
 import { query } from 'web/util/index'
-import Vue from './web-runtime'
 import { compileToFunctions } from './web-compiler'
 
 const idToTemplate = cached(id => query(id).innerHTML)

+ 2 - 3
src/server/create-renderer.js

@@ -1,6 +1,5 @@
 /* @flow */
 
-import type Vue from 'core/instance/index'
 import RenderStream from './render-stream'
 import { createRenderFunction } from './render'
 import { warn } from 'core/util/debug'
@@ -30,7 +29,7 @@ export function createRenderer ({
 
   return {
     renderToString (
-      component: Vue,
+      component: Component,
       done: (err: ?Error, res: ?string) => any
     ): void {
       let result = ''
@@ -58,7 +57,7 @@ export function createRenderer ({
       }
     },
 
-    renderToStream (component: Vue): RenderStream {
+    renderToStream (component: Component): RenderStream {
       return new RenderStream((write, done) => {
         render(component, write, done)
       })

+ 1 - 2
src/server/render.js

@@ -1,6 +1,5 @@
 /* @flow */
 
-import type Vue from 'core/instance/index'
 import type VNode from 'core/vdom/vnode'
 import { createComponentInstanceForVnode } from 'core/vdom/create-component'
 
@@ -82,7 +81,7 @@ export function createRenderFunction (
     return markup + '>'
   }
 
-  return function render (component: Vue, write: Function, done: Function) {
+  return function render (component: Component, write: Function, done: Function) {
     renderNode(component._render(), write, done, true)
   }
 }