wip: props proxy for setup()
This commit is contained in:
parent
e665a133e9
commit
333ceaa4b5
@ -1,5 +1,10 @@
|
|||||||
import { VNode, normalizeVNode, VNodeChild } from './vnode'
|
import { VNode, normalizeVNode, VNodeChild } from './vnode'
|
||||||
import { ReactiveEffect, UnwrapValue, observable } from '@vue/observer'
|
import {
|
||||||
|
ReactiveEffect,
|
||||||
|
UnwrapValue,
|
||||||
|
observable,
|
||||||
|
immutable
|
||||||
|
} from '@vue/observer'
|
||||||
import { isFunction, EMPTY_OBJ } from '@vue/shared'
|
import { isFunction, EMPTY_OBJ } from '@vue/shared'
|
||||||
import { RenderProxyHandlers } from './componentProxy'
|
import { RenderProxyHandlers } from './componentProxy'
|
||||||
import { ComponentPropsOptions, PropValidator } from './componentProps'
|
import { ComponentPropsOptions, PropValidator } from './componentProps'
|
||||||
@ -79,7 +84,8 @@ export type ComponentInstance<P = Data, S = Data> = {
|
|||||||
update: ReactiveEffect
|
update: ReactiveEffect
|
||||||
effects: ReactiveEffect[] | null
|
effects: ReactiveEffect[] | null
|
||||||
// the rest are only for stateful components
|
// the rest are only for stateful components
|
||||||
proxy: ComponentPublicProperties | null
|
renderProxy: ComponentPublicProperties | null
|
||||||
|
propsProxy: Data | null
|
||||||
state: S
|
state: S
|
||||||
props: P
|
props: P
|
||||||
attrs: Data
|
attrs: Data
|
||||||
@ -109,7 +115,8 @@ export function createComponentInstance(type: any): ComponentInstance {
|
|||||||
next: null,
|
next: null,
|
||||||
subTree: null as any,
|
subTree: null as any,
|
||||||
update: null as any,
|
update: null as any,
|
||||||
proxy: null,
|
renderProxy: null,
|
||||||
|
propsProxy: null,
|
||||||
|
|
||||||
bm: null,
|
bm: null,
|
||||||
m: null,
|
m: null,
|
||||||
@ -138,28 +145,37 @@ export let currentInstance: ComponentInstance | null = null
|
|||||||
export function setupStatefulComponent(instance: ComponentInstance) {
|
export function setupStatefulComponent(instance: ComponentInstance) {
|
||||||
const Component = instance.type as ComponentOptions
|
const Component = instance.type as ComponentOptions
|
||||||
// 1. create render proxy
|
// 1. create render proxy
|
||||||
const proxy = (instance.proxy = new Proxy(
|
const proxy = (instance.renderProxy = new Proxy(
|
||||||
instance,
|
instance,
|
||||||
RenderProxyHandlers
|
RenderProxyHandlers
|
||||||
) as any)
|
) as any)
|
||||||
// 2. call setup()
|
// 2. call setup()
|
||||||
if (Component.setup) {
|
const { setup } = Component
|
||||||
|
if (setup) {
|
||||||
currentInstance = instance
|
currentInstance = instance
|
||||||
// TODO should pass reactive props here
|
// the props proxy makes the props object passed to setup() reactive
|
||||||
instance.state = observable(Component.setup.call(proxy, instance.props))
|
// so props change can be tracked by watchers
|
||||||
|
// only need to create it if setup() actually expects it
|
||||||
|
// it will be updated in resolveProps() on updates before render
|
||||||
|
const propsProxy = (instance.propsProxy = setup.length
|
||||||
|
? immutable(instance.props)
|
||||||
|
: null)
|
||||||
|
instance.state = observable(setup.call(proxy, propsProxy))
|
||||||
currentInstance = null
|
currentInstance = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderComponentRoot(instance: ComponentInstance): VNode {
|
export function renderComponentRoot(instance: ComponentInstance): VNode {
|
||||||
const { type: Component, proxy } = instance
|
const { type: Component, renderProxy } = instance
|
||||||
if (isFunction(Component)) {
|
if (isFunction(Component)) {
|
||||||
return normalizeVNode(Component(instance))
|
return normalizeVNode(Component(instance))
|
||||||
} else {
|
} else {
|
||||||
if (__DEV__ && !Component.render) {
|
if (__DEV__ && !Component.render) {
|
||||||
// TODO warn missing render
|
// TODO warn missing render
|
||||||
}
|
}
|
||||||
return normalizeVNode((Component.render as Function).call(proxy, instance))
|
return normalizeVNode(
|
||||||
|
(Component.render as Function).call(renderProxy, instance)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { immutable, unwrap } from '@vue/observer'
|
import { immutable, unwrap, lock, unlock } from '@vue/observer'
|
||||||
import {
|
import {
|
||||||
EMPTY_OBJ,
|
EMPTY_OBJ,
|
||||||
camelize,
|
camelize,
|
||||||
@ -61,8 +61,25 @@ export function resolveProps(
|
|||||||
if (!rawProps && !hasDeclaredProps) {
|
if (!rawProps && !hasDeclaredProps) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const props: any = {}
|
const props: any = {}
|
||||||
let attrs: any = void 0
|
let attrs: any = void 0
|
||||||
|
|
||||||
|
// update the instance propsProxy (passed to setup()) to trigger potential
|
||||||
|
// changes
|
||||||
|
const propsProxy = instance.propsProxy
|
||||||
|
const setProp = propsProxy
|
||||||
|
? (key: string, val: any) => {
|
||||||
|
props[key] = val
|
||||||
|
propsProxy[key] = val
|
||||||
|
}
|
||||||
|
: (key: string, val: any) => {
|
||||||
|
props[key] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow mutation of propsProxy (which is immutable by default)
|
||||||
|
unlock()
|
||||||
|
|
||||||
if (rawProps != null) {
|
if (rawProps != null) {
|
||||||
for (const key in rawProps) {
|
for (const key in rawProps) {
|
||||||
// key, ref, slots are reserved
|
// key, ref, slots are reserved
|
||||||
@ -74,7 +91,7 @@ export function resolveProps(
|
|||||||
if (hasDeclaredProps && !options.hasOwnProperty(key)) {
|
if (hasDeclaredProps && !options.hasOwnProperty(key)) {
|
||||||
;(attrs || (attrs = {}))[key] = rawProps[key]
|
;(attrs || (attrs = {}))[key] = rawProps[key]
|
||||||
} else {
|
} else {
|
||||||
props[key] = rawProps[key]
|
setProp(key, rawProps[key])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,17 +106,17 @@ export function resolveProps(
|
|||||||
// default values
|
// default values
|
||||||
if (hasDefault && currentValue === undefined) {
|
if (hasDefault && currentValue === undefined) {
|
||||||
const defaultValue = opt.default
|
const defaultValue = opt.default
|
||||||
props[key] = isFunction(defaultValue) ? defaultValue() : defaultValue
|
setProp(key, isFunction(defaultValue) ? defaultValue() : defaultValue)
|
||||||
}
|
}
|
||||||
// boolean casting
|
// boolean casting
|
||||||
if (opt[BooleanFlags.shouldCast]) {
|
if (opt[BooleanFlags.shouldCast]) {
|
||||||
if (isAbsent && !hasDefault) {
|
if (isAbsent && !hasDefault) {
|
||||||
props[key] = false
|
setProp(key, false)
|
||||||
} else if (
|
} else if (
|
||||||
opt[BooleanFlags.shouldCastTrue] &&
|
opt[BooleanFlags.shouldCastTrue] &&
|
||||||
(currentValue === '' || currentValue === hyphenate(key))
|
(currentValue === '' || currentValue === hyphenate(key))
|
||||||
) {
|
) {
|
||||||
props[key] = true
|
setProp(key, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// runtime validation
|
// runtime validation
|
||||||
@ -112,6 +129,9 @@ export function resolveProps(
|
|||||||
attrs = props
|
attrs = props
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lock immutable
|
||||||
|
lock()
|
||||||
|
|
||||||
instance.props = __DEV__ ? immutable(props) : props
|
instance.props = __DEV__ ? immutable(props) : props
|
||||||
instance.attrs = options
|
instance.attrs = options
|
||||||
? __DEV__
|
? __DEV__
|
||||||
|
@ -442,7 +442,7 @@ export function createRenderer(options: RendererOptions) {
|
|||||||
instance.vnode = vnode
|
instance.vnode = vnode
|
||||||
resolveProps(instance, vnode.props, Component.props)
|
resolveProps(instance, vnode.props, Component.props)
|
||||||
// setup stateful
|
// setup stateful
|
||||||
if (typeof Component === 'object' && Component.setup) {
|
if (typeof Component === 'object') {
|
||||||
setupStatefulComponent(instance)
|
setupStatefulComponent(instance)
|
||||||
}
|
}
|
||||||
const subTree = (instance.subTree = renderComponentRoot(instance))
|
const subTree = (instance.subTree = renderComponentRoot(instance))
|
||||||
|
Loading…
Reference in New Issue
Block a user