]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: component setup flow
authorEvan You <yyx990803@gmail.com>
Wed, 29 May 2019 03:36:16 +0000 (11:36 +0800)
committerEvan You <yyx990803@gmail.com>
Wed, 29 May 2019 03:36:16 +0000 (11:36 +0800)
packages/runtime-core/src/component.ts
packages/runtime-core/src/componentProps.ts
packages/runtime-core/src/createRenderer.ts

index b7a1ea3e22a5bcaa957cd74f49ec5150a8c2e62f..b2a980fc2889ef8a5e1483e4ffc20bdf641f99c3 100644 (file)
@@ -2,12 +2,7 @@ import { VNode, normalizeVNode, VNodeChild } from './vnode'
 import { ReactiveEffect } from '@vue/observer'
 import { isFunction, EMPTY_OBJ } from '@vue/shared'
 import { RenderProxyHandlers } from './componentProxy'
-import {
-  resolveProps,
-  ComponentPropsOptions,
-  initializeProps,
-  PropValidator
-} from './componentProps'
+import { ComponentPropsOptions, PropValidator } from './componentProps'
 
 interface Value<T> {
   value: T
@@ -37,13 +32,6 @@ export interface ComponentPublicProperties<P = Data, S = Data> {
   $slots: Data
 }
 
-interface RenderFunctionArg<B = Data, P = Data> {
-  state: B
-  props: P
-  attrs: Data
-  slots: Slots
-}
-
 export interface ComponentOptions<
   RawProps = ComponentPropsOptions,
   RawBindings = Data | void,
@@ -54,22 +42,16 @@ export interface ComponentOptions<
   setup?: (props: Props) => RawBindings
   render?: <B extends Bindings>(
     this: ComponentPublicProperties<Props, B>,
-    ctx: RenderFunctionArg<B, Props>
+    ctx: ComponentInstance<Props, B>
   ) => VNodeChild
 }
 
 export interface FunctionalComponent<P = {}> {
-  (ctx: RenderFunctionArg): any
+  (ctx: ComponentInstance<P>): any
   props?: ComponentPropsOptions<P>
   displayName?: string
 }
 
-export type Slot = (...args: any[]) => VNode[]
-
-export type Slots = Readonly<{
-  [name: string]: Slot
-}>
-
 type LifecycleHook = Function[] | null
 
 export interface LifecycleHooks {
@@ -86,18 +68,26 @@ export interface LifecycleHooks {
   ec: LifecycleHook // errorCaptured
 }
 
-export type ComponentInstance = {
+export type Slot = (...args: any[]) => VNode[]
+
+export type Slots = Readonly<{
+  [name: string]: Slot
+}>
+
+export type ComponentInstance<P = Data, S = Data> = {
   type: FunctionalComponent | ComponentOptions
   vnode: VNode
   next: VNode | null
   subTree: VNode
   update: ReactiveEffect
-
   // the rest are only for stateful components
-  bindings: Data | null
-  proxy: Data | null
-} & LifecycleHooks &
-  ComponentPublicProperties
+  proxy: ComponentPublicProperties | null
+  state: S
+  props: P
+  attrs: Data
+  slots: Slots
+  refs: Data
+} & LifecycleHooks
 
 // no-op, for type inference only
 export function createComponent<
@@ -121,7 +111,6 @@ export function createComponentInstance(type: any): ComponentInstance {
     next: null,
     subTree: null as any,
     update: null as any,
-    bindings: null,
     proxy: null,
 
     bm: null,
@@ -137,64 +126,41 @@ export function createComponentInstance(type: any): ComponentInstance {
     ec: null,
 
     // public properties
-    $attrs: EMPTY_OBJ,
-    $props: EMPTY_OBJ,
-    $refs: EMPTY_OBJ,
-    $slots: EMPTY_OBJ,
-    $state: EMPTY_OBJ
+    state: EMPTY_OBJ,
+    props: EMPTY_OBJ,
+    attrs: EMPTY_OBJ,
+    slots: EMPTY_OBJ,
+    refs: EMPTY_OBJ
   }
 }
 
 export let currentInstance: ComponentInstance | null = null
 
-export function setupStatefulComponent(
-  instance: ComponentInstance,
-  props: Data | null
-) {
+export function setupStatefulComponent(instance: ComponentInstance) {
   const Component = instance.type as ComponentOptions
   // 1. create render proxy
-  const proxy = (instance.proxy = new Proxy(instance, RenderProxyHandlers))
-  // 2. resolve initial props
-  initializeProps(instance, Component.props, props)
-  // 3. call setup()
+  const proxy = (instance.proxy = new Proxy(
+    instance,
+    RenderProxyHandlers
+  ) as any)
+  // 2. call setup()
   if (Component.setup) {
     currentInstance = instance
-    instance.bindings = Component.setup.call(proxy, proxy)
+    // TODO should pass reactive props here
+    instance.state = Component.setup.call(proxy, instance.props)
     currentInstance = null
   }
 }
 
-export function renderComponentRoot(
-  instance: ComponentInstance,
-  useAlreadyResolvedProps?: boolean
-): VNode {
-  const { type, vnode, proxy, bindings, $slots } = instance
-  const renderArg: RenderFunctionArg = {
-    state: bindings || EMPTY_OBJ,
-    slots: $slots,
-    props: null as any,
-    attrs: null as any
-  }
-  if (useAlreadyResolvedProps) {
-    // initial render for stateful components with setup()
-    // props are already resolved
-    renderArg.props = instance.$props
-    renderArg.attrs = instance.$attrs
-  } else {
-    const { 0: props, 1: attrs } = resolveProps(
-      (vnode as VNode).props,
-      type.props
-    )
-    instance.$props = renderArg.props = props
-    instance.$attrs = renderArg.attrs = attrs
-  }
-  if (isFunction(type)) {
-    return normalizeVNode(type(renderArg))
+export function renderComponentRoot(instance: ComponentInstance): VNode {
+  const { type: Component, proxy } = instance
+  if (isFunction(Component)) {
+    return normalizeVNode(Component(instance))
   } else {
-    if (__DEV__ && !type.render) {
+    if (__DEV__ && !Component.render) {
       // TODO warn missing render
     }
-    return normalizeVNode((type.render as Function).call(proxy, renderArg))
+    return normalizeVNode((Component.render as Function).call(proxy, instance))
   }
 }
 
index 89a71e7baeb8a0d200e209bfe2aeb184b418bff2..3b013540b487a3a5e6afd939fda81490dc491270 100644 (file)
@@ -43,20 +43,6 @@ type NormalizedPropsOptions = Record<string, NormalizedProp>
 
 const isReservedKey = (key: string): boolean => key[0] === '_' || key[0] === '$'
 
-export function initializeProps(
-  instance: ComponentInstance,
-  options: ComponentPropsOptions | undefined,
-  rawProps: Data | null
-) {
-  const { 0: props, 1: attrs } = resolveProps(rawProps, options)
-  instance.$props = __DEV__ ? immutable(props) : props
-  instance.$attrs = options
-    ? __DEV__
-      ? immutable(attrs)
-      : attrs
-    : instance.$props
-}
-
 // resolve raw VNode data.
 // - filter out reserved keys (key, ref, slots)
 // - extract class and style into $attrs (to be merged onto child
@@ -65,16 +51,15 @@ export function initializeProps(
 //   - if has declared props: put declared ones in `props`, the rest in `attrs`
 //   - else: everything goes in `props`.
 
-const EMPTY_PROPS = [EMPTY_OBJ, EMPTY_OBJ] as [Data, Data]
-
 export function resolveProps(
+  instance: ComponentInstance,
   rawProps: any,
   _options: ComponentPropsOptions | void
-): [Data, Data] {
+) {
   const hasDeclaredProps = _options != null
   const options = normalizePropsOptions(_options) as NormalizedPropsOptions
   if (!rawProps && !hasDeclaredProps) {
-    return EMPTY_PROPS
+    return
   }
   const props: any = {}
   let attrs: any = void 0
@@ -126,7 +111,13 @@ export function resolveProps(
     // if component has no declared props, $attrs === $props
     attrs = props
   }
-  return [props, attrs]
+
+  instance.props = __DEV__ ? immutable(props) : props
+  instance.attrs = options
+    ? __DEV__
+      ? immutable(attrs)
+      : attrs
+    : instance.props
 }
 
 const normalizationMap = new WeakMap()
index ba3caa06203909a32f06a43f938a03d1727fc593..52d0c1df843595f9143089c13efb6f9b51ae68c1 100644 (file)
@@ -18,6 +18,7 @@ import { isString, isArray, EMPTY_OBJ, EMPTY_ARR } from '@vue/shared'
 import { TEXT, CLASS, STYLE, PROPS, KEYED, UNKEYED } from './patchFlags'
 import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
 import { effect, stop, ReactiveEffectOptions } from '@vue/observer'
+import { resolveProps } from './componentProps'
 
 const prodEffectOptions = {
   scheduler: queueJob
@@ -367,18 +368,16 @@ export function createRenderer(options: RendererOptions) {
     const instance: ComponentInstance = (vnode.component = createComponentInstance(
       Component
     ))
-    const needsSetup = typeof Component === 'object' && Component.setup
-    if (needsSetup) {
-      setupStatefulComponent(instance, vnode.props)
-    }
     instance.update = effect(() => {
       if (!instance.vnode) {
         // initial mount
         instance.vnode = vnode
-        const subTree = (instance.subTree = renderComponentRoot(
-          instance,
-          needsSetup
-        ))
+        resolveProps(instance, vnode.props, Component.props)
+        // setup stateful
+        if (typeof Component === 'object' && Component.setup) {
+          setupStatefulComponent(instance)
+        }
+        const subTree = (instance.subTree = renderComponentRoot(instance))
         // beforeMount hook
         if (instance.bm !== null) {
           invokeHooks(instance.bm)
@@ -396,6 +395,7 @@ export function createRenderer(options: RendererOptions) {
           next.component = instance
           instance.vnode = next
           instance.next = null
+          resolveProps(instance, next.props, Component.props)
         }
         const prevTree = instance.subTree
         const nextTree = (instance.subTree = renderComponentRoot(instance))