From: Evan You Date: Wed, 10 Oct 2018 00:22:29 +0000 (-0400) Subject: refactor: use flat options on class X-Git-Tag: v3.0.0-alpha.0~1131 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=da6f0d7adc405f679364efc3b61176cbf8939472;p=thirdparty%2Fvuejs%2Fcore.git refactor: use flat options on class --- diff --git a/packages/core/__tests__/attrsFallthrough.spec.ts b/packages/core/__tests__/attrsFallthrough.spec.ts index a6df61ceb5..7d59e19eb4 100644 --- a/packages/core/__tests__/attrsFallthrough.spec.ts +++ b/packages/core/__tests__/attrsFallthrough.spec.ts @@ -95,10 +95,8 @@ describe('attribute fallthrough', () => { } class Child extends Component<{ foo: number }> { - static options = { - props: { - foo: Number - } + static props = { + foo: Number } updated() { childUpdated() @@ -169,10 +167,8 @@ describe('attribute fallthrough', () => { } class Child extends Component { - static options = { - props: { - foo: Number - } + static props = { + foo: Number } updated() { childUpdated() @@ -183,10 +179,8 @@ describe('attribute fallthrough', () => { } class GrandChild extends Component<{ foo: number }> { - static options = { - props: { - foo: Number - } + static props = { + foo: Number } updated() { grandChildUpdated() diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index 5099b39675..9f01c284b8 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -3,6 +3,7 @@ import { VNode, Slots, RenderNode, MountedVNode } from './vdom' import { Data, ComponentOptions, + ComponentClassOptions, ComponentPropsOptions, WatchOptions } from './componentOptions' @@ -10,10 +11,10 @@ import { setupWatcher } from './componentWatch' import { Autorun, DebuggerEvent, ComputedGetter } from '@vue/observer' import { nextTick } from '@vue/scheduler' import { ErrorTypes } from './errorHandling' +import { resolveComponentOptions } from './componentUtils' -type Flatten = { [K in keyof T]: T[K] } - -export interface ComponentClass extends Flatten { +export interface ComponentClass extends ComponentClassOptions { + options?: ComponentOptions new

(): MergedComponent } @@ -30,15 +31,15 @@ export interface FunctionalComponent

{ export type ComponentType = ComponentClass | FunctionalComponent export interface ComponentInstance

extends InternalComponent { + constructor: ComponentClass + $vnode: MountedVNode $data: D $props: Readonly

$attrs: Data - $computed: Data $slots: Slots $root: ComponentInstance $children: ComponentInstance[] - $options: ComponentOptions data?(): Partial render(props: Readonly

, slots: Slots, attrs: Data): any @@ -70,8 +71,6 @@ export interface ComponentInstance

extends InternalComponent { } class InternalComponent { - public static options?: ComponentOptions - public get $el(): RenderNode | null { return this.$vnode && this.$vnode.el } @@ -81,12 +80,11 @@ class InternalComponent { public $data: Data | null = null public $props: Data | null = null public $attrs: Data | null = null - public $computed: Data | null = null public $slots: Slots | null = null public $root: ComponentInstance | null = null public $parent: ComponentInstance | null = null public $children: ComponentInstance[] = [] - public $options: any + public $options: ComponentOptions public $refs: Record = {} public $proxy: any = null public $forceUpdate: (() => void) | null = null @@ -103,12 +101,10 @@ class InternalComponent { public _isVue: boolean = true public _inactiveRoot: boolean = false - constructor(options?: ComponentOptions) { - this.$options = options || (this.constructor as any).options || EMPTY_OBJ - // root instance - if (options !== void 0) { - // mount this - } + constructor() { + this.$options = + (this.constructor as ComponentClass).options || + resolveComponentOptions(this.constructor as ComponentClass) } $nextTick(fn: () => any): Promise { diff --git a/packages/core/src/componentComputed.ts b/packages/core/src/componentComputed.ts index 19fd0ad0dd..fa44269530 100644 --- a/packages/core/src/componentComputed.ts +++ b/packages/core/src/componentComputed.ts @@ -1,21 +1,12 @@ -import { EMPTY_OBJ, NOOP } from './utils' +import { NOOP } from './utils' import { computed, stop, ComputedGetter } from '@vue/observer' import { ComponentClass, ComponentInstance } from './component' import { ComponentComputedOptions } from './componentOptions' -const extractionCache: WeakMap< - ComponentClass, - ComponentComputedOptions -> = new WeakMap() - -export function getComputedOptions( +export function resolveComputedOptions( comp: ComponentClass ): ComponentComputedOptions { - let computedOptions = extractionCache.get(comp) - if (computedOptions) { - return computedOptions - } - computedOptions = {} + const computedOptions: ComponentComputedOptions = {} const descriptors = Object.getOwnPropertyDescriptors(comp.prototype as any) for (const key in descriptors) { const d = descriptors[key] @@ -25,7 +16,6 @@ export function getComputedOptions( // as it's already defined on the prototype } } - extractionCache.set(comp, computedOptions) return computedOptions } @@ -34,7 +24,6 @@ export function initializeComputed( computedOptions: ComponentComputedOptions | undefined ) { if (!computedOptions) { - instance.$computed = EMPTY_OBJ return } const handles: Record< @@ -47,17 +36,6 @@ export function initializeComputed( const getter = typeof option === 'function' ? option : option.get || NOOP handles[key] = computed(getter, proxy) } - instance.$computed = new Proxy( - {}, - { - get(_, key: any) { - if (handles.hasOwnProperty(key)) { - return handles[key]() - } - } - // TODO should be readonly - } - ) } export function teardownComputed(instance: ComponentInstance) { diff --git a/packages/core/src/componentOptions.ts b/packages/core/src/componentOptions.ts index 60bd1cb306..a75148bad8 100644 --- a/packages/core/src/componentOptions.ts +++ b/packages/core/src/componentOptions.ts @@ -3,14 +3,18 @@ import { Slots } from './vdom' export type Data = Record -export interface ComponentOptions { - data?(): object +export interface ComponentClassOptions { props?: ComponentPropsOptions computed?: ComponentComputedOptions watch?: ComponentWatchOptions - render?: (this: This, props: Readonly, slots: Slots, attrs: Data) => any - inheritAttrs?: boolean displayName?: string + inheritAttrs?: boolean +} + +export interface ComponentOptions + extends ComponentClassOptions { + data?(): object + render?: (this: This, props: Readonly, slots: Slots, attrs: Data) => any // TODO other options readonly [key: string]: any } diff --git a/packages/core/src/componentProps.ts b/packages/core/src/componentProps.ts index 17c4252617..fe95aa8184 100644 --- a/packages/core/src/componentProps.ts +++ b/packages/core/src/componentProps.ts @@ -18,9 +18,10 @@ import { export function initializeProps( instance: ComponentInstance, + options: ComponentPropsOptions | undefined, data: Data | null ) { - const { props, attrs } = resolveProps(data, instance.$options.props) + const { props, attrs } = resolveProps(data, options) instance.$props = immutable(props || {}) instance.$attrs = immutable(attrs || {}) } @@ -32,7 +33,7 @@ export function updateProps(instance: ComponentInstance, nextData: Data) { if (nextData != null) { const { props: nextProps, attrs: nextAttrs } = resolveProps( nextData, - instance.$options.props + instance.constructor.props ) // unlock to temporarily allow mutatiing props unlock() diff --git a/packages/core/src/componentProxy.ts b/packages/core/src/componentProxy.ts index 33dc6ff5af..43bfd7251c 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.$options.props != null && - target.$options.props.hasOwnProperty(key) + target.constructor.props != null && + target.constructor.props.hasOwnProperty(key) ) { // props are only proxied if declared return target.$props[key] @@ -61,8 +61,8 @@ const renderProxyHandlers = { return false } if ( - target.$options.props != null && - target.$options.props.hasOwnProperty(key) + target.constructor.props != null && + target.constructor.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 740c4e2c6a..04d8705cee 100644 --- a/packages/core/src/componentUtils.ts +++ b/packages/core/src/componentUtils.ts @@ -8,7 +8,7 @@ import { initializeState } from './componentState' import { initializeProps } from './componentProps' import { initializeComputed, - getComputedOptions, + resolveComputedOptions, teardownComputed } from './componentComputed' import { initializeWatch, teardownWatch } from './componentWatch' @@ -40,11 +40,10 @@ export function createComponentInstance( if (instance.beforeCreate) { instance.beforeCreate.call(proxy) } - // TODO provide/inject - initializeProps(instance, vnode.data) + initializeProps(instance, Component.props, vnode.data) initializeState(instance) - initializeComputed(instance, getComputedOptions(Component)) - initializeWatch(instance, instance.$options.watch) + initializeComputed(instance, Component.computed) + initializeWatch(instance, Component.watch) instance.$slots = vnode.slots || EMPTY_OBJ if (instance.created) { instance.created.call(proxy) @@ -75,7 +74,7 @@ export function renderInstanceRoot(instance: ComponentInstance): VNode { vnode, instance.$parentVNode, instance.$attrs, - instance.$options.inheritAttrs + instance.constructor.inheritAttrs ) } @@ -171,36 +170,40 @@ export function createComponentClassFromOptions( options: ComponentOptions ): ComponentClass { class AnonymousComponent extends Component { - constructor() { - super() - this.$options = options - } + static options = options } const proto = AnonymousComponent.prototype as any for (const key in options) { const value = options[key] // name -> displayName - if (__COMPAT__ && key === 'name') { - options.displayName = options.name - } - if (typeof value === 'function') { - if (__COMPAT__ && key === 'render') { - proto[key] = function() { - return value.call(this, h) + if (key === 'name') { + AnonymousComponent.displayName = options.name + } else if (typeof value === 'function') { + if (__COMPAT__) { + if (key === 'render') { + proto[key] = function() { + return value.call(this, h) + } + } else if (key === 'beforeDestroy') { + proto.beforeUnmount = value + } else if (key === 'destroyed') { + proto.unmounted = value } } else { proto[key] = value } - } - if (key === 'computed') { - const isGet = typeof value === 'function' - Object.defineProperty(proto, key, { - configurable: true, - get: isGet ? value : value.get, - set: isGet ? undefined : value.set - }) - } - if (key === 'methods') { + } else if (key === 'computed') { + AnonymousComponent.computed = value + for (const computedKey in value) { + const computed = value[computedKey] + const isGet = typeof computed === 'function' + Object.defineProperty(proto, computedKey, { + configurable: true, + get: isGet ? computed : computed.get, + set: isGet ? undefined : computed.set + }) + } + } else if (key === 'methods') { for (const method in value) { if (__DEV__ && proto.hasOwnProperty(method)) { console.warn( @@ -210,7 +213,23 @@ export function createComponentClassFromOptions( } proto[method] = value[method] } + } else { + ;(AnonymousComponent as any)[key] = value } } return AnonymousComponent as ComponentClass } + +export function resolveComponentOptions( + Component: ComponentClass +): ComponentOptions { + const keys = Object.keys(Component) + const options = {} as any + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + options[key] = (Component as any)[key] + } + Component.computed = options.computed = resolveComputedOptions(Component) + Component.options = options + return options +} diff --git a/packages/core/src/optional/context.ts b/packages/core/src/optional/context.ts index a260370db8..595972f453 100644 --- a/packages/core/src/optional/context.ts +++ b/packages/core/src/optional/context.ts @@ -9,6 +9,16 @@ interface ProviderProps { } export class Provide extends Component { + static props = { + id: { + type: [String, Symbol], + required: true + }, + value: { + required: true + } + } + updateValue() { // TS doesn't allow symbol as index :/ // https://github.com/Microsoft/TypeScript/issues/24587 @@ -43,18 +53,6 @@ export class Provide extends Component { } } -Provide.options = { - props: { - id: { - type: [String, Symbol], - required: true - }, - value: { - required: true - } - } -} - export class Inject extends Component { render(props: any, slots: any) { return slots.default && slots.default(contextStore[props.id]) diff --git a/packages/core/src/optional/keepAlive.ts b/packages/core/src/optional/keepAlive.ts index 17d6a19866..ec95e6552e 100644 --- a/packages/core/src/optional/keepAlive.ts +++ b/packages/core/src/optional/keepAlive.ts @@ -114,7 +114,7 @@ export class KeepAlive extends Component { ;(KeepAlive as any)[KeepAliveSymbol] = true function getName(comp: ComponentClass): string | void { - return comp.options && comp.options.name + return comp.displayName || comp.name } function matches(pattern: MatchPattern, name: string): boolean {