]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: props proxy for setup()
authorEvan You <yyx990803@gmail.com>
Thu, 30 May 2019 15:16:15 +0000 (23:16 +0800)
committerEvan You <yyx990803@gmail.com>
Thu, 30 May 2019 15:16:15 +0000 (23:16 +0800)
packages/runtime-core/src/component.ts
packages/runtime-core/src/componentProps.ts
packages/runtime-core/src/createRenderer.ts

index 6f61660edf72011178e47e4a0bcc11fe42458779..af5af2292d750f406b99531e47d6abd90d0c8afd 100644 (file)
@@ -1,5 +1,10 @@
 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 { RenderProxyHandlers } from './componentProxy'
 import { ComponentPropsOptions, PropValidator } from './componentProps'
@@ -79,7 +84,8 @@ export type ComponentInstance<P = Data, S = Data> = {
   update: ReactiveEffect
   effects: ReactiveEffect[] | null
   // the rest are only for stateful components
-  proxy: ComponentPublicProperties | null
+  renderProxy: ComponentPublicProperties | null
+  propsProxy: Data | null
   state: S
   props: P
   attrs: Data
@@ -109,7 +115,8 @@ export function createComponentInstance(type: any): ComponentInstance {
     next: null,
     subTree: null as any,
     update: null as any,
-    proxy: null,
+    renderProxy: null,
+    propsProxy: null,
 
     bm: null,
     m: null,
@@ -138,28 +145,37 @@ export let currentInstance: ComponentInstance | null = null
 export function setupStatefulComponent(instance: ComponentInstance) {
   const Component = instance.type as ComponentOptions
   // 1. create render proxy
-  const proxy = (instance.proxy = new Proxy(
+  const proxy = (instance.renderProxy = new Proxy(
     instance,
     RenderProxyHandlers
   ) as any)
   // 2. call setup()
-  if (Component.setup) {
+  const { setup } = Component
+  if (setup) {
     currentInstance = instance
-    // TODO should pass reactive props here
-    instance.state = observable(Component.setup.call(proxy, instance.props))
+    // the props proxy makes the props object passed to setup() reactive
+    // 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
   }
 }
 
 export function renderComponentRoot(instance: ComponentInstance): VNode {
-  const { type: Component, proxy } = instance
+  const { type: Component, renderProxy } = instance
   if (isFunction(Component)) {
     return normalizeVNode(Component(instance))
   } else {
     if (__DEV__ && !Component.render) {
       // TODO warn missing render
     }
-    return normalizeVNode((Component.render as Function).call(proxy, instance))
+    return normalizeVNode(
+      (Component.render as Function).call(renderProxy, instance)
+    )
   }
 }
 
index 3b013540b487a3a5e6afd939fda81490dc491270..e2aad8dabc4db93c02ff6aa66bf7668ace2aa5c6 100644 (file)
@@ -1,4 +1,4 @@
-import { immutable, unwrap } from '@vue/observer'
+import { immutable, unwrap, lock, unlock } from '@vue/observer'
 import {
   EMPTY_OBJ,
   camelize,
@@ -61,8 +61,25 @@ export function resolveProps(
   if (!rawProps && !hasDeclaredProps) {
     return
   }
+
   const props: any = {}
   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) {
     for (const key in rawProps) {
       // key, ref, slots are reserved
@@ -74,7 +91,7 @@ export function resolveProps(
       if (hasDeclaredProps && !options.hasOwnProperty(key)) {
         ;(attrs || (attrs = {}))[key] = rawProps[key]
       } else {
-        props[key] = rawProps[key]
+        setProp(key, rawProps[key])
       }
     }
   }
@@ -89,17 +106,17 @@ export function resolveProps(
       // default values
       if (hasDefault && currentValue === undefined) {
         const defaultValue = opt.default
-        props[key] = isFunction(defaultValue) ? defaultValue() : defaultValue
+        setProp(key, isFunction(defaultValue) ? defaultValue() : defaultValue)
       }
       // boolean casting
       if (opt[BooleanFlags.shouldCast]) {
         if (isAbsent && !hasDefault) {
-          props[key] = false
+          setProp(key, false)
         } else if (
           opt[BooleanFlags.shouldCastTrue] &&
           (currentValue === '' || currentValue === hyphenate(key))
         ) {
-          props[key] = true
+          setProp(key, true)
         }
       }
       // runtime validation
@@ -112,6 +129,9 @@ export function resolveProps(
     attrs = props
   }
 
+  // lock immutable
+  lock()
+
   instance.props = __DEV__ ? immutable(props) : props
   instance.attrs = options
     ? __DEV__
index 60505a428771a15be76b8e84b7c5e2a94a830403..77a8ea391b7229b2497bfe9c7658d9b22250311f 100644 (file)
@@ -442,7 +442,7 @@ export function createRenderer(options: RendererOptions) {
         instance.vnode = vnode
         resolveProps(instance, vnode.props, Component.props)
         // setup stateful
-        if (typeof Component === 'object' && Component.setup) {
+        if (typeof Component === 'object') {
           setupStatefulComponent(instance)
         }
         const subTree = (instance.subTree = renderComponentRoot(instance))