From b08b15dba0625f48df7d83444804797399e788b4 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 26 Oct 2018 17:49:40 -0400 Subject: [PATCH] refactor: adjust props initialization/updating --- packages/runtime-core/src/componentProps.ts | 65 ++++++++++++--------- packages/runtime-core/src/componentUtils.ts | 4 +- packages/runtime-core/src/createRenderer.ts | 4 +- 3 files changed, 39 insertions(+), 34 deletions(-) diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index da1fa825..e8733d68 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -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( diff --git a/packages/runtime-core/src/componentUtils.ts b/packages/runtime-core/src/componentUtils.ts index 54154a3c..52fea6c0 100644 --- a/packages/runtime-core/src/componentUtils.ts +++ b/packages/runtime-core/src/componentUtils.ts @@ -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) } diff --git a/packages/runtime-core/src/createRenderer.ts b/packages/runtime-core/src/createRenderer.ts index b4e6f60e..db06a64c 100644 --- a/packages/runtime-core/src/createRenderer.ts +++ b/packages/runtime-core/src/createRenderer.ts @@ -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