import { VNode, normalizeVNode, VNodeChild } from './vnode'
-import { ReactiveEffect } from '@vue/observer'
+import { ReactiveEffect, observable } from '@vue/observer'
import { isFunction, EMPTY_OBJ } from '@vue/shared'
import { RenderProxyHandlers } from './componentProxy'
import { ComponentPropsOptions, PropValidator } from './componentProps'
// TODO
$refs: Data
$slots: Data
+
+ $root: ComponentInstance | null
+ $parent: ComponentInstance | null
}
export interface ComponentOptions<
if (Component.setup) {
currentInstance = instance
// TODO should pass reactive props here
- instance.state = Component.setup.call(proxy, instance.props)
+ instance.state = observable(Component.setup.call(proxy, instance.props))
currentInstance = null
}
}
-export const RenderProxyHandlers = {}
+import { ComponentInstance } from './component'
+import { isObservable, unwrap } from '@vue/observer'
+
+// TODO use proper implementation
+function isValue(binding: any) {
+ return isObservable(binding) && unwrap(binding).hasOwnProperty('value')
+}
+
+export const RenderProxyHandlers = {
+ get(target: ComponentInstance, key: string) {
+ const { state, props } = target
+ if (state.hasOwnProperty(key)) {
+ const value = state[key]
+ return isValue(value) ? value.value : value
+ } else if (props.hasOwnProperty(key)) {
+ return props[key]
+ } else {
+ switch (key) {
+ case '$state':
+ return target.state
+ case '$props':
+ return target.props
+ case '$attrs':
+ return target.attrs
+ case '$slots':
+ return target.slots
+ case '$refs':
+ return target.refs
+ default:
+ break
+ }
+ }
+ },
+ set(target: ComponentInstance, key: string, value: any): boolean {
+ const { state } = target
+ if (state.hasOwnProperty(key)) {
+ const binding = state[key]
+ if (isValue(binding)) {
+ binding.value = value
+ } else {
+ state[key] = value
+ }
+ return true
+ } else {
+ if (__DEV__) {
+ if (key[0] === '$') {
+ // TODO warn attempt of mutating public property
+ } else if (target.props.hasOwnProperty(key)) {
+ // TODO warn attempt of mutating prop
+ }
+ }
+ }
+ return false
+ }
+}
queuePostFlushCb(instance.m)
}
} else {
- // this is triggered by processComponent with `next` already set
+ // component update
+ // This is triggered by mutation of component's own state (next: null)
+ // OR parent calling processComponent (next: VNode)
const { next } = instance
if (next != null) {
next.component = instance
instance.vnode = next
instance.next = null
resolveProps(instance, next.props, Component.props)
+ // TODO slots
}
const prevTree = instance.subTree
const nextTree = (instance.subTree = renderComponentRoot(instance))
: getNextHostNode(vnode.component.subTree)
}
- return function render(vnode: VNode, dom: HostNode): VNode {
- patch(dom._vnode, vnode, dom)
+ return function render(vnode: VNode | null, dom: HostNode): VNode | null {
+ if (vnode == null) {
+ if (dom._vnode) {
+ unmount(dom._vnode, true)
+ }
+ } else {
+ patch(dom._vnode, vnode, dom)
+ }
flushPostFlushCbs()
return (dom._vnode = vnode)
}