refactor: use flat options on class
This commit is contained in:
parent
b527705928
commit
da6f0d7adc
@ -95,11 +95,9 @@ describe('attribute fallthrough', () => {
|
||||
}
|
||||
|
||||
class Child extends Component<{ foo: number }> {
|
||||
static options = {
|
||||
props: {
|
||||
static props = {
|
||||
foo: Number
|
||||
}
|
||||
}
|
||||
updated() {
|
||||
childUpdated()
|
||||
}
|
||||
@ -169,11 +167,9 @@ describe('attribute fallthrough', () => {
|
||||
}
|
||||
|
||||
class Child extends Component {
|
||||
static options = {
|
||||
props: {
|
||||
static props = {
|
||||
foo: Number
|
||||
}
|
||||
}
|
||||
updated() {
|
||||
childUpdated()
|
||||
}
|
||||
@ -183,11 +179,9 @@ describe('attribute fallthrough', () => {
|
||||
}
|
||||
|
||||
class GrandChild extends Component<{ foo: number }> {
|
||||
static options = {
|
||||
props: {
|
||||
static props = {
|
||||
foo: Number
|
||||
}
|
||||
}
|
||||
updated() {
|
||||
grandChildUpdated()
|
||||
}
|
||||
|
@ -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<T> = { [K in keyof T]: T[K] }
|
||||
|
||||
export interface ComponentClass extends Flatten<typeof InternalComponent> {
|
||||
export interface ComponentClass extends ComponentClassOptions {
|
||||
options?: ComponentOptions
|
||||
new <P extends object = {}, D extends object = {}>(): MergedComponent<P, D>
|
||||
}
|
||||
|
||||
@ -30,15 +31,15 @@ export interface FunctionalComponent<P = {}> {
|
||||
export type ComponentType = ComponentClass | FunctionalComponent
|
||||
|
||||
export interface ComponentInstance<P = {}, D = {}> extends InternalComponent {
|
||||
constructor: ComponentClass
|
||||
|
||||
$vnode: MountedVNode
|
||||
$data: D
|
||||
$props: Readonly<P>
|
||||
$attrs: Data
|
||||
$computed: Data
|
||||
$slots: Slots
|
||||
$root: ComponentInstance
|
||||
$children: ComponentInstance[]
|
||||
$options: ComponentOptions<this>
|
||||
|
||||
data?(): Partial<D>
|
||||
render(props: Readonly<P>, slots: Slots, attrs: Data): any
|
||||
@ -70,8 +71,6 @@ export interface ComponentInstance<P = {}, D = {}> 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<string, ComponentInstance | RenderNode> = {}
|
||||
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<any> {
|
||||
|
@ -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) {
|
||||
|
@ -3,14 +3,18 @@ import { Slots } from './vdom'
|
||||
|
||||
export type Data = Record<string, any>
|
||||
|
||||
export interface ComponentOptions<This = ComponentInstance> {
|
||||
data?(): object
|
||||
export interface ComponentClassOptions<This = ComponentInstance> {
|
||||
props?: ComponentPropsOptions
|
||||
computed?: ComponentComputedOptions<This>
|
||||
watch?: ComponentWatchOptions<This>
|
||||
render?: (this: This, props: Readonly<Data>, slots: Slots, attrs: Data) => any
|
||||
inheritAttrs?: boolean
|
||||
displayName?: string
|
||||
inheritAttrs?: boolean
|
||||
}
|
||||
|
||||
export interface ComponentOptions<This = ComponentInstance>
|
||||
extends ComponentClassOptions<This> {
|
||||
data?(): object
|
||||
render?: (this: This, props: Readonly<Data>, slots: Slots, attrs: Data) => any
|
||||
// TODO other options
|
||||
readonly [key: string]: any
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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') {
|
||||
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, {
|
||||
} 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 ? value : value.get,
|
||||
set: isGet ? undefined : value.set
|
||||
get: isGet ? computed : computed.get,
|
||||
set: isGet ? undefined : computed.set
|
||||
})
|
||||
}
|
||||
if (key === 'methods') {
|
||||
} 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
|
||||
}
|
||||
|
@ -9,6 +9,16 @@ interface ProviderProps {
|
||||
}
|
||||
|
||||
export class Provide extends Component<ProviderProps> {
|
||||
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<ProviderProps> {
|
||||
}
|
||||
}
|
||||
|
||||
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])
|
||||
|
@ -114,7 +114,7 @@ export class KeepAlive extends Component<KeepAliveProps> {
|
||||
;(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 {
|
||||
|
Loading…
Reference in New Issue
Block a user