]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: adjust props initialization/updating
authorEvan You <yyx990803@gmail.com>
Fri, 26 Oct 2018 21:49:40 +0000 (17:49 -0400)
committerEvan You <yyx990803@gmail.com>
Fri, 26 Oct 2018 21:49:40 +0000 (17:49 -0400)
packages/runtime-core/src/componentProps.ts
packages/runtime-core/src/componentUtils.ts
packages/runtime-core/src/createRenderer.ts

index da1fa8254e1ba81241246629801679dec166213f..e8733d68f2ae34c56ad1765befa835ef6eea1ad6 100644 (file)
@@ -19,8 +19,6 @@ import {
 } from '@vue/shared'
 import { warn } from './warning'
 
-const EMPTY_PROPS = { props: EMPTY_OBJ }
-
 const enum BooleanFlags {
   shouldCast = '1',
   shouldCastTrue = '2'
@@ -38,9 +36,11 @@ export function initializeProps(
   options: NormalizedPropsOptions | undefined,
   data: Data | null
 ) {
-  const { props, attrs } = resolveProps(data, options)
-  instance.$props = immutable(props || {})
-  instance.$attrs = immutable(attrs || {})
+  const [props, attrs] = resolveProps(data, options)
+  instance.$props = immutable(props === EMPTY_OBJ ? {} : props)
+  instance.$attrs = options
+    ? immutable(attrs === EMPTY_OBJ ? {} : attrs)
+    : instance.$props
 }
 
 // resolve raw VNode data.
@@ -50,10 +50,13 @@ export function initializeProps(
 // - for the rest:
 //   - 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(
   rawData: any,
   _options: NormalizedPropsOptions | void
-): { props: Data; attrs?: Data } {
+): [Data, Data] {
   const hasDeclaredProps = _options !== void 0
   const options = _options as NormalizedPropsOptions
   if (!rawData && !hasDeclaredProps) {
@@ -109,44 +112,48 @@ export function resolveProps(
     // if component has no declared props, $attrs === $props
     attrs = props
   }
-  return { props, attrs }
+  return [props, attrs]
 }
 
-export function updateProps(instance: ComponentInstance, nextData: Data) {
+export function updateProps(
+  instance: ComponentInstance,
+  nextData: Data | null
+) {
   // instance.$props and instance.$attrs are observables that should not be
   // replaced. Instead, we mutate them to match latest props, which will trigger
   // updates if any value that's been used in child component has changed.
-  if (nextData != null) {
-    const { props: nextProps, attrs: nextAttrs } = resolveProps(
-      nextData,
-      instance.$options.props
-    )
-    // unlock to temporarily allow mutatiing props
-    unlock()
-    const props = instance.$props
-    const rawProps = unwrap(props)
-    for (const key in rawProps) {
-      if (!nextProps.hasOwnProperty(key)) {
-        delete (props as any)[key]
-      }
+  const [nextProps, nextAttrs] = resolveProps(nextData, instance.$options.props)
+  // unlock to temporarily allow mutatiing props
+  unlock()
+  const props = instance.$props
+  const rawProps = unwrap(props)
+  const hasEmptyProps = nextProps === EMPTY_OBJ
+  for (const key in rawProps) {
+    if (hasEmptyProps || !nextProps.hasOwnProperty(key)) {
+      delete (props as any)[key]
     }
+  }
+  if (!hasEmptyProps) {
     for (const key in nextProps) {
       ;(props as any)[key] = nextProps[key]
     }
-    if (nextAttrs) {
-      const attrs = instance.$attrs
-      const rawAttrs = unwrap(attrs)
-      for (const key in rawAttrs) {
-        if (!nextAttrs.hasOwnProperty(key)) {
-          delete attrs[key]
-        }
+  }
+  const attrs = instance.$attrs
+  if (attrs !== props) {
+    const rawAttrs = unwrap(attrs)
+    const hasEmptyAttrs = nextAttrs === EMPTY_OBJ
+    for (const key in rawAttrs) {
+      if (hasEmptyAttrs || !nextAttrs.hasOwnProperty(key)) {
+        delete attrs[key]
       }
+    }
+    if (!hasEmptyAttrs) {
       for (const key in nextAttrs) {
         attrs[key] = nextAttrs[key]
       }
     }
-    lock()
   }
+  lock()
 }
 
 export function normalizePropsOptions(
index 54154a3c7a3831b6518c1f4eb8ddfce26093fdd3..52fea6c0b110f71a0cb008e467d1435aa8f936c1 100644 (file)
@@ -119,10 +119,10 @@ export function renderInstanceRoot(instance: ComponentInstance): VNode {
 
 export function renderFunctionalRoot(vnode: VNode): VNode {
   const render = vnode.tag as FunctionalComponent
-  const { props, attrs } = resolveProps(vnode.data, render.props)
+  const [props, attrs] = resolveProps(vnode.data, render.props)
   let subTree
   try {
-    subTree = render(props, vnode.slots || EMPTY_OBJ, attrs || EMPTY_OBJ, vnode)
+    subTree = render(props, vnode.slots || EMPTY_OBJ, attrs, vnode)
   } catch (err) {
     handleError(err, vnode, ErrorTypes.RENDER)
   }
index b4e6f60e0e45050bef3450bce7a8c1815ef7dc9a..db06a64cd6b4c3bdd34e76e501f483de3f11e667 100644 (file)
@@ -480,9 +480,7 @@ export function createRenderer(options: RendererOptions) {
     instance.$parentVNode = nextVNode as MountedVNode
 
     // Update props. This will trigger child update if necessary.
-    if (nextData !== null) {
-      updateProps(instance, nextData)
-    }
+    updateProps(instance, nextData)
 
     // If has different slots content, or has non-compiled slots,
     // the child needs to be force updated. It's ok to call $forceUpdate