From: HcySunYang Date: Thu, 25 Mar 2021 21:26:58 +0000 (+0800) Subject: fix(runtime-core): cache props default values to avoid unnecessary watcher trigger... X-Git-Tag: v3.0.8~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=44166b43d9be1062f79612880f71284049bcab0b;p=thirdparty%2Fvuejs%2Fcore.git fix(runtime-core): cache props default values to avoid unnecessary watcher trigger (#3474) fix #3471 --- diff --git a/packages/runtime-core/__tests__/componentProps.spec.ts b/packages/runtime-core/__tests__/componentProps.spec.ts index 001e0d5da1..7c90a54a12 100644 --- a/packages/runtime-core/__tests__/componentProps.spec.ts +++ b/packages/runtime-core/__tests__/componentProps.spec.ts @@ -10,7 +10,8 @@ import { serializeInner, createApp, provide, - inject + inject, + watch } from '@vue/runtime-test' import { render as domRender, nextTick } from 'vue' @@ -420,4 +421,43 @@ describe('component props', () => { expect(serializeInner(root)).toMatch('
60000000100000111
') }) + + // #3474 + test('should cache the value returned from the default factory to avoid unnecessary watcher trigger', async () => { + let count = 0 + const Comp = { + props: { + foo: { + type: Object, + default: () => ({ val: 1 }) + }, + bar: Number + }, + setup(props: any) { + watch( + () => props.foo, + () => { + count++ + } + ) + return () => h('h1', [props.foo.val, props.bar]) + } + } + + const foo = ref() + const bar = ref(0) + const app = createApp({ + render: () => h(Comp, { foo: foo.value, bar: bar.value }) + }) + + const root = nodeOps.createElement('div') + app.mount(root) + expect(serializeInner(root)).toMatch(`

10

`) + expect(count).toBe(0) + + bar.value++ + await nextTick() + expect(serializeInner(root)).toMatch(`

11

`) + expect(count).toBe(0) + }) }) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 79847225bd..51147ccc8f 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -302,7 +302,12 @@ export interface ComponentInternalInstance { * @internal */ emitted: Record | null - + /** + * used for caching the value returned from props default factory functions to + * avoid unnecessary watcher trigger + * @internal + */ + propsDefaults: Data /** * setup related * @internal @@ -440,6 +445,9 @@ export function createComponentInstance( emit: null as any, // to be set immediately emitted: null, + // props default value + propsDefaults: EMPTY_OBJ, + // state ctx: EMPTY_OBJ, data: EMPTY_OBJ, diff --git a/packages/runtime-core/src/componentProps.ts b/packages/runtime-core/src/componentProps.ts index 91190231fb..39c15471f8 100644 --- a/packages/runtime-core/src/componentProps.ts +++ b/packages/runtime-core/src/componentProps.ts @@ -139,6 +139,9 @@ export function initProps( const props: Data = {} const attrs: Data = {} def(attrs, InternalObjectKey, 1) + + instance.propsDefaults = Object.create(null) + setFullProps(instance, rawProps, props, attrs) // validation if (__DEV__) { @@ -326,9 +329,14 @@ function resolvePropValue( if (hasDefault && value === undefined) { const defaultValue = opt.default if (opt.type !== Function && isFunction(defaultValue)) { - setCurrentInstance(instance) - value = defaultValue(props) - setCurrentInstance(null) + const { propsDefaults } = instance + if (key in propsDefaults) { + value = propsDefaults[key] + } else { + setCurrentInstance(instance) + value = propsDefaults[key] = defaultValue(props) + setCurrentInstance(null) + } } else { value = defaultValue }