refactor: access all options from instance.$options

This commit is contained in:
Evan You 2018-10-16 12:37:31 -04:00
parent 62214fa42e
commit 7bc28a6e61
6 changed files with 41 additions and 44 deletions

View File

@ -99,6 +99,7 @@ export interface ComponentInstance<P = {}, D = {}>
$slots: Slots $slots: Slots
$root: ComponentInstance $root: ComponentInstance
$children: ComponentInstance[] $children: ComponentInstance[]
$options: ComponentOptions<P, D>
_updateHandle: Autorun _updateHandle: Autorun
_queueJob: ((fn: () => void) => void) _queueJob: ((fn: () => void) => void)
@ -120,7 +121,7 @@ class InternalComponent implements PublicInstanceMethods {
$root: ComponentInstance | null = null $root: ComponentInstance | null = null
$parent: ComponentInstance | null = null $parent: ComponentInstance | null = null
$children: ComponentInstance[] = [] $children: ComponentInstance[] = []
$options: ComponentOptions $options: ComponentOptions | null = null
$refs: Record<string, ComponentInstance | RenderNode> = {} $refs: Record<string, ComponentInstance | RenderNode> = {}
$proxy: any = null $proxy: any = null

View File

@ -1,24 +1,8 @@
import { NOOP } from './utils' import { NOOP } from './utils'
import { computed, stop, ComputedGetter } from '@vue/observer' import { computed, stop, ComputedGetter } from '@vue/observer'
import { ComponentClass, ComponentInstance } from './component' import { ComponentInstance } from './component'
import { ComponentComputedOptions } from './componentOptions' 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( export function initializeComputed(
instance: ComponentInstance, instance: ComponentInstance,
computedOptions: ComponentComputedOptions | undefined computedOptions: ComponentComputedOptions | undefined

View File

@ -1,4 +1,4 @@
import { ComponentInstance, Component } from './component' import { ComponentInstance } from './component'
import { Slots } from './vdom' import { Slots } from './vdom'
export type Data = Record<string, any> export type Data = Record<string, any>
@ -10,8 +10,11 @@ export interface ComponentClassOptions<P = {}, This = ComponentInstance> {
displayName?: string displayName?: string
} }
export interface ComponentOptions<P = {}, D = {}, This = Component<P, D>> export interface ComponentOptions<
extends ComponentClassOptions<P, This> { P = {},
D = {},
This = ComponentInstance<P, D>
> extends ComponentClassOptions<P, This> {
data?(): D data?(): D
render?: (this: This, props: Readonly<Data>, slots: Slots, attrs: Data) => any render?: (this: This, props: Readonly<Data>, slots: Slots, attrs: Data) => any
// TODO other options // TODO other options

View File

@ -99,7 +99,7 @@ export function updateProps(instance: ComponentInstance, nextData: Data) {
if (nextData != null) { if (nextData != null) {
const { props: nextProps, attrs: nextAttrs } = resolveProps( const { props: nextProps, attrs: nextAttrs } = resolveProps(
nextData, nextData,
instance.constructor.props instance.$options.props
) )
// unlock to temporarily allow mutatiing props // unlock to temporarily allow mutatiing props
unlock() unlock()

View File

@ -25,8 +25,8 @@ const renderProxyHandlers = {
// data // data
return target.$data[key] return target.$data[key]
} else if ( } else if (
target.constructor.props != null && target.$options.props != null &&
target.constructor.props.hasOwnProperty(key) target.$options.props.hasOwnProperty(key)
) { ) {
// props are only proxied if declared // props are only proxied if declared
return target.$props[key] return target.$props[key]
@ -61,8 +61,8 @@ const renderProxyHandlers = {
return false return false
} }
if ( if (
target.constructor.props != null && target.$options.props != null &&
target.constructor.props.hasOwnProperty(key) target.$options.props.hasOwnProperty(key)
) { ) {
// TODO warn props are immutable // TODO warn props are immutable
return false return false

View File

@ -11,11 +11,7 @@ import {
import { createTextVNode, cloneVNode } from './vdom' import { createTextVNode, cloneVNode } from './vdom'
import { initializeState } from './componentState' import { initializeState } from './componentState'
import { initializeProps, resolveProps } from './componentProps' import { initializeProps, resolveProps } from './componentProps'
import { import { initializeComputed, teardownComputed } from './componentComputed'
initializeComputed,
resolveComputedOptions,
teardownComputed
} from './componentComputed'
import { initializeWatch, teardownWatch } from './componentWatch' import { initializeWatch, teardownWatch } from './componentWatch'
import { ComponentOptions } from './componentOptions' import { ComponentOptions } from './componentOptions'
import { createRenderProxy } from './componentProxy' import { createRenderProxy } from './componentProxy'
@ -42,8 +38,8 @@ export function createComponentInstance(
// then we finish the initialization by collecting properties set on the // then we finish the initialization by collecting properties set on the
// instance // instance
initializeState(instance) initializeState(instance)
initializeComputed(instance, Component.computed) initializeComputed(instance, instance.$options.computed)
initializeWatch(instance, Component.watch) initializeWatch(instance, instance.$options.watch)
instance.$slots = currentVNode.slots || EMPTY_OBJ instance.$slots = currentVNode.slots || EMPTY_OBJ
if (instance.created) { if (instance.created) {
instance.created.call(instance.$proxy) instance.created.call(instance.$proxy)
@ -93,7 +89,7 @@ export function initializeComponentInstance(instance: ComponentInstance) {
} }
initializeProps( initializeProps(
instance, instance,
instance.constructor.props, instance.$options.props,
(currentVNode as VNode).data (currentVNode as VNode).data
) )
} }
@ -212,8 +208,9 @@ export function createComponentClassFromOptions(
const value = options[key] const value = options[key]
// name -> displayName // name -> displayName
if (key === 'name') { if (key === 'name') {
AnonymousComponent.displayName = options.name options.displayName = options.name
} else if (typeof value === 'function') { } else if (typeof value === 'function') {
// lifecycle hook / data / render
if (__COMPAT__) { if (__COMPAT__) {
if (key === 'render') { if (key === 'render') {
proto[key] = function() { proto[key] = function() {
@ -230,7 +227,6 @@ export function createComponentClassFromOptions(
proto[key] = value proto[key] = value
} }
} else if (key === 'computed') { } else if (key === 'computed') {
AnonymousComponent.computed = value
for (const computedKey in value) { for (const computedKey in value) {
const computed = value[computedKey] const computed = value[computedKey]
const isGet = typeof computed === 'function' const isGet = typeof computed === 'function'
@ -250,28 +246,41 @@ export function createComponentClassFromOptions(
} }
proto[method] = value[method] proto[method] = value[method]
} }
} else {
;(AnonymousComponent as any)[key] = value
} }
} }
return AnonymousComponent as ComponentClass 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( export function resolveComponentOptions(
Component: ComponentClass Component: ComponentClass
): ComponentOptions { ): ComponentOptions {
if (Component.options) { if (Component.options) {
return Component.options return Component.options
} }
const descriptors = Object.getOwnPropertyDescriptors(Component) const staticDescriptors = Object.getOwnPropertyDescriptors(Component)
const options = {} as any const options = {} as any
for (const key in descriptors) { for (const key in staticDescriptors) {
const descriptor = descriptors[key] const { enumerable, get, value } = staticDescriptors[key]
if (descriptor.enumerable || descriptor.get) { if (enumerable || get) {
options[key] = descriptor.get ? descriptor.get() : descriptor.value 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 Component.options = options
return options return options
} }