From 1b7ab89a5deb49fbdaec8fb474108863714f99a7 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 13 May 2016 18:30:04 -0400 Subject: [PATCH] rework type annotation strategy: use interface instead --- .flowconfig | 1 + flow/declarations.js | 116 +++++++++++++++++++ src/core/global-api/assets.js | 12 +- src/core/global-api/extend.js | 8 +- src/core/global-api/index.js | 3 +- src/core/global-api/mixin.js | 3 +- src/core/global-api/use.js | 3 +- src/core/index.js | 4 +- src/core/instance/events.js | 50 ++++---- src/core/instance/index.js | 141 ++--------------------- src/core/instance/init.js | 34 ++++++ src/core/instance/lifecycle.js | 104 ++++++++--------- src/core/instance/render.js | 35 +++--- src/core/instance/state.js | 77 +++++++------ src/core/observer/index.js | 18 ++- src/core/observer/watcher.js | 5 +- src/core/util/options.js | 4 +- src/core/util/props.js | 7 +- src/entries/web-runtime-with-compiler.js | 2 +- src/server/create-renderer.js | 5 +- src/server/render.js | 3 +- 21 files changed, 334 insertions(+), 301 deletions(-) create mode 100644 flow/declarations.js create mode 100644 src/core/instance/init.js diff --git a/.flowconfig b/.flowconfig index 6ea1eeaa..97177440 100644 --- a/.flowconfig +++ b/.flowconfig @@ -8,6 +8,7 @@ [include] [libs] +flow [options] module.name_mapper='^compiler/\(.*\)$' -> '/src/compiler/\1' diff --git a/flow/declarations.js b/flow/declarations.js new file mode 100644 index 00000000..7255d74c --- /dev/null +++ b/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 | Object) => Class; + 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; + $refs: { [key: string]: Component | Element | Array | void }; + $slots: { [key: string]: Array }; + $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) => Component; + $nextTick: (fn: Function) => void; + $createElement: ( + tag?: string | Component, + data?: Object, + children?: Array | string, + namespace?: string + ) => VNode; + + // private properties + _uid: number; + _isVue: true; + _renderProxy: Component; + _watcher: Watcher; + _watchers: Array; + _data: Object; + _events: Object; + _isMounted: boolean; + _isDestroyed: boolean; + _isBeingDestroyed: boolean; + _vnode: ?VNode; + _staticTrees: ?Array; + + // private methods + // lifecycle + _mount: () => Component; + _update: (vnode: VNode) => void; + _updateFromParent: ( + propsData?: Object, + listeners?: { [key: string]: Function | Array }, + parentVnode: VNode, + renderChildren: Array | () => Array + ) => void; + // rendering + _render: () => VNode; + __patch__: (a: Element | VNode | void, b: VNode) => Element; + __h__: ( + tag?: string | Component | Object, + data?: Object, + children?: Array | string, + namespace?: string + ) => VNode; + __toString__: (value: any) => string; + __resolveFilter__: (id: string) => Function; + __renderList__: ( + val: any, + render: Function + ) => ?Array; + __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 }; + + directive: (id: string, def?: Function | Object) => Function | Object | void; + component: (id: string, def?: Class | Object) => Class; + transition: (id: string, def?: Object) => Object | void; + filter: (id: string, def?: Function) => Function | void; + + // allow dynamic method registration + [key: string]: any +} diff --git a/src/core/global-api/assets.js b/src/core/global-api/assets.js index 5d228622..e194f27b 100644 --- a/src/core/global-api/assets.js +++ b/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 { diff --git a/src/core/global-api/extend.js b/src/core/global-api/extend.js index 9df84a6f..f11caca5 100644 --- a/src/core/global-api/extend.js +++ b/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) { +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) { /** * Class inheritance */ - Vue.extend = function (extendOptions: Object): Class { + 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) { } } const Sub = function VueComponent (options) { - this._init(options) + init(this, options) } Sub.prototype = Object.create(Super.prototype) Sub.prototype.constructor = Sub diff --git a/src/core/global-api/index.js b/src/core/global-api/index.js index 2a7c6609..1275d696 100644 --- a/src/core/global-api/index.js +++ b/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) { +export function initGlobalAPI (Vue: GlobalAPI) { Vue.config = config Vue.util = util Vue.set = set diff --git a/src/core/global-api/mixin.js b/src/core/global-api/mixin.js index 8fab3928..cc7b02e4 100644 --- a/src/core/global-api/mixin.js +++ b/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) { +export function initMixin (Vue: GlobalAPI) { Vue.mixin = function (mixin: Object) { Vue.options = mergeOptions(Vue.options, mixin) } diff --git a/src/core/global-api/use.js b/src/core/global-api/use.js index ee447b3e..db5cd296 100644 --- a/src/core/global-api/use.js +++ b/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) { +export function initUse (Vue: GlobalAPI) { Vue.use = function (plugin: Function | Object) { /* istanbul ignore if */ if (plugin.installed) { diff --git a/src/core/index.js b/src/core/index.js index 50e1b1f7..c0f5e4b8 100644 --- a/src/core/index.js +++ b/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 }) diff --git a/src/core/instance/events.js b/src/core/instance/events.js index b425c8b2..6867a5b9 100644 --- a/src/core/instance/events.js +++ b/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.prototype.$on = function (event: string, fn: Function): Vue { - (this._events[event] || (this._events[event] = [])).push(fn) - return this +export function eventsMixin (Vue: Class) { + 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) { 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 } } diff --git a/src/core/instance/index.js b/src/core/instance/index.js index 6411ffcd..7d72b93e 100644 --- a/src/core/instance/index.js +++ b/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; - static compile: (template: string) => { render: Function, staticRenderFns: Array }; - - // assets - static directive: (id: string, def?: Function | Object) => Function | Object | void; - static component: (id: string, def?: Class | Object) => Class; - 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; - $refs: { [key: string]: Vue | Element }; - $slots: { [key: string]: Array }; - $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) => Vue; - $nextTick: (fn: Function) => void; - $createElement: ( - tag?: string | Vue, - data?: Object, - children?: Array | string, - namespace?: string - ) => VNode; - - // private properties - _uid: number; - _isVue: true; - _renderProxy: Vue; - _watchers: Array; - _data: Object; - _events: { [key: string]: Array }; - _isMounted: boolean; - _isDestroyed: boolean; - _isBeingDestroyed: boolean; - _vnode: ?VNode; - _staticTrees: ?Array; - - // private methods - // lifecycle - _mount: () => Vue; - _update: (vnode: VNode) => void; - _updateFromParent: (propsData?: Object, listeners?: Object, parentVnode: VNode, renderChildren: Array | () => Array) => void; - // rendering - _render: () => VNode; - __h__: ( - tag?: string | Vue, - data?: Object, - children?: Array | string, - namespace?: string - ) => VNode; - __toString__: (value: any) => string; - __resolveFilter__: (id: string) => Function; - __renderList__: ( - val: any, - render: Function - ) => ?Array; - __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 diff --git a/src/core/instance/init.js b/src/core/instance/init.js new file mode 100644 index 00000000..1a0d8be0 --- /dev/null +++ b/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) +} diff --git a/src/core/instance/lifecycle.js b/src/core/instance/lifecycle.js index 7ab25e6f..02d203f9 100644 --- a/src/core/instance/lifecycle.js +++ b/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.prototype._mount = function (): Vue { - if (!this.$options.render) { - this.$options.render = () => this.$createElement('div') +export function lifecycleMixin (Vue: Class) { + 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) { parentVnode: VNode, renderChildren: Array | () => Array ) { - 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) { diff --git a/src/core/instance/render.js b/src/core/instance/render.js index 4ef11b7e..3f670fda 100644 --- a/src/core/instance/render.js +++ b/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.prototype.$nextTick = function (fn) { +export function renderMixin (Vue: Class) { + 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) { 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) { } } -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 | void) { +function resolveSlots (vm: Component, renderChildren: () => Array | void) { if (renderChildren) { const children = flatten(renderChildren()) const slots = {} diff --git a/src/core/instance/state.js b/src/core/instance/state.js index e6b528b3..0b70637e 100644 --- a/src/core/instance/state.js +++ b/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) { + // 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 diff --git a/src/core/observer/index.js b/src/core/observer/index.js index b537b40c..c4c317eb 100644 --- a/src/core/observer/index.js +++ b/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; + vms: ?Array; 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) { 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] } diff --git a/src/core/observer/watcher.js b/src/core/observer/watcher.js index 7b19394f..20c76ae7 100644 --- a/src/core/observer/watcher.js +++ b/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 = {} diff --git a/src/core/util/options.js b/src/core/util/options.js index dcd32fde..d42ad16a 100644 --- a/src/core/util/options.js +++ b/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) diff --git a/src/core/util/props.js b/src/core/util/props.js index 80f14860..b1c9cd2d 100644 --- a/src/core/util/props.js +++ b/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) { diff --git a/src/entries/web-runtime-with-compiler.js b/src/entries/web-runtime-with-compiler.js index 3cc33dd2..c34d3002 100644 --- a/src/entries/web-runtime-with-compiler.js +++ b/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) diff --git a/src/server/create-renderer.js b/src/server/create-renderer.js index 7d5cb43f..c4edea1b 100644 --- a/src/server/create-renderer.js +++ b/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) }) diff --git a/src/server/render.js b/src/server/render.js index 5f6a03a1..d76cfa4b 100644 --- a/src/server/render.js +++ b/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) } }