diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index 6d5d4bfa..ee62fd22 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -99,6 +99,7 @@ export interface ComponentInstance

$slots: Slots $root: ComponentInstance $children: ComponentInstance[] + $options: ComponentOptions _updateHandle: Autorun _queueJob: ((fn: () => void) => void) @@ -120,7 +121,7 @@ class InternalComponent implements PublicInstanceMethods { $root: ComponentInstance | null = null $parent: ComponentInstance | null = null $children: ComponentInstance[] = [] - $options: ComponentOptions + $options: ComponentOptions | null = null $refs: Record = {} $proxy: any = null diff --git a/packages/core/src/componentComputed.ts b/packages/core/src/componentComputed.ts index fa442695..4f3e99c2 100644 --- a/packages/core/src/componentComputed.ts +++ b/packages/core/src/componentComputed.ts @@ -1,24 +1,8 @@ import { NOOP } from './utils' import { computed, stop, ComputedGetter } from '@vue/observer' -import { ComponentClass, ComponentInstance } from './component' +import { ComponentInstance } from './component' import { ComponentComputedOptions } from './componentOptions' -export function resolveComputedOptions( - comp: ComponentClass -): ComponentComputedOptions { - const computedOptions: ComponentComputedOptions = {} - const descriptors = Object.getOwnPropertyDescriptors(comp.prototype as any) - for (const key in descriptors) { - const d = descriptors[key] - if (d.get) { - computedOptions[key] = d.get - // there's no need to do anything for the setter - // as it's already defined on the prototype - } - } - return computedOptions -} - export function initializeComputed( instance: ComponentInstance, computedOptions: ComponentComputedOptions | undefined diff --git a/packages/core/src/componentOptions.ts b/packages/core/src/componentOptions.ts index bd436fb9..bab81745 100644 --- a/packages/core/src/componentOptions.ts +++ b/packages/core/src/componentOptions.ts @@ -1,4 +1,4 @@ -import { ComponentInstance, Component } from './component' +import { ComponentInstance } from './component' import { Slots } from './vdom' export type Data = Record @@ -10,8 +10,11 @@ export interface ComponentClassOptions

{ displayName?: string } -export interface ComponentOptions

> - extends ComponentClassOptions { +export interface ComponentOptions< + P = {}, + D = {}, + This = ComponentInstance +> extends ComponentClassOptions { data?(): D render?: (this: This, props: Readonly, slots: Slots, attrs: Data) => any // TODO other options diff --git a/packages/core/src/componentProps.ts b/packages/core/src/componentProps.ts index 7a4e7897..9dae489c 100644 --- a/packages/core/src/componentProps.ts +++ b/packages/core/src/componentProps.ts @@ -99,7 +99,7 @@ export function updateProps(instance: ComponentInstance, nextData: Data) { if (nextData != null) { const { props: nextProps, attrs: nextAttrs } = resolveProps( nextData, - instance.constructor.props + instance.$options.props ) // unlock to temporarily allow mutatiing props unlock() diff --git a/packages/core/src/componentProxy.ts b/packages/core/src/componentProxy.ts index 43bfd725..33dc6ff5 100644 --- a/packages/core/src/componentProxy.ts +++ b/packages/core/src/componentProxy.ts @@ -25,8 +25,8 @@ const renderProxyHandlers = { // data return target.$data[key] } else if ( - target.constructor.props != null && - target.constructor.props.hasOwnProperty(key) + target.$options.props != null && + target.$options.props.hasOwnProperty(key) ) { // props are only proxied if declared return target.$props[key] @@ -61,8 +61,8 @@ const renderProxyHandlers = { return false } if ( - target.constructor.props != null && - target.constructor.props.hasOwnProperty(key) + target.$options.props != null && + target.$options.props.hasOwnProperty(key) ) { // TODO warn props are immutable return false diff --git a/packages/core/src/componentUtils.ts b/packages/core/src/componentUtils.ts index 8893a905..b277c822 100644 --- a/packages/core/src/componentUtils.ts +++ b/packages/core/src/componentUtils.ts @@ -11,11 +11,7 @@ import { import { createTextVNode, cloneVNode } from './vdom' import { initializeState } from './componentState' import { initializeProps, resolveProps } from './componentProps' -import { - initializeComputed, - resolveComputedOptions, - teardownComputed -} from './componentComputed' +import { initializeComputed, teardownComputed } from './componentComputed' import { initializeWatch, teardownWatch } from './componentWatch' import { ComponentOptions } from './componentOptions' import { createRenderProxy } from './componentProxy' @@ -42,8 +38,8 @@ export function createComponentInstance( // then we finish the initialization by collecting properties set on the // instance initializeState(instance) - initializeComputed(instance, Component.computed) - initializeWatch(instance, Component.watch) + initializeComputed(instance, instance.$options.computed) + initializeWatch(instance, instance.$options.watch) instance.$slots = currentVNode.slots || EMPTY_OBJ if (instance.created) { instance.created.call(instance.$proxy) @@ -93,7 +89,7 @@ export function initializeComponentInstance(instance: ComponentInstance) { } initializeProps( instance, - instance.constructor.props, + instance.$options.props, (currentVNode as VNode).data ) } @@ -212,8 +208,9 @@ export function createComponentClassFromOptions( const value = options[key] // name -> displayName if (key === 'name') { - AnonymousComponent.displayName = options.name + options.displayName = options.name } else if (typeof value === 'function') { + // lifecycle hook / data / render if (__COMPAT__) { if (key === 'render') { proto[key] = function() { @@ -230,7 +227,6 @@ export function createComponentClassFromOptions( proto[key] = value } } else if (key === 'computed') { - AnonymousComponent.computed = value for (const computedKey in value) { const computed = value[computedKey] const isGet = typeof computed === 'function' @@ -250,28 +246,41 @@ export function createComponentClassFromOptions( } proto[method] = value[method] } - } else { - ;(AnonymousComponent as any)[key] = value } } return AnonymousComponent as ComponentClass } +// This is called in the base component constructor and the return value is +// set on the instance as $options. export function resolveComponentOptions( Component: ComponentClass ): ComponentOptions { if (Component.options) { return Component.options } - const descriptors = Object.getOwnPropertyDescriptors(Component) + const staticDescriptors = Object.getOwnPropertyDescriptors(Component) const options = {} as any - for (const key in descriptors) { - const descriptor = descriptors[key] - if (descriptor.enumerable || descriptor.get) { - options[key] = descriptor.get ? descriptor.get() : descriptor.value + for (const key in staticDescriptors) { + const { enumerable, get, value } = staticDescriptors[key] + if (enumerable || get) { + options[key] = get ? get() : value + } + } + const instanceDescriptors = Object.getOwnPropertyDescriptors( + Component.prototype + ) + for (const key in instanceDescriptors) { + const { get, value } = instanceDescriptors[key] + if (get) { + // computed properties + ;(options.computed || (options.computed = {}))[key] = get + // there's no need to do anything for the setter + // as it's already defined on the prototype + } else if (typeof value === 'function') { + ;(options.methods || (options.methods = {}))[key] = value } } - Component.computed = options.computed = resolveComputedOptions(Component) Component.options = options return options }