fix(runtime-core): cache props default values to avoid unnecessary watcher trigger (#3474)
fix #3471
This commit is contained in:
parent
ebedcccdc0
commit
44166b43d9
@ -10,7 +10,8 @@ import {
|
|||||||
serializeInner,
|
serializeInner,
|
||||||
createApp,
|
createApp,
|
||||||
provide,
|
provide,
|
||||||
inject
|
inject,
|
||||||
|
watch
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
import { render as domRender, nextTick } from 'vue'
|
import { render as domRender, nextTick } from 'vue'
|
||||||
|
|
||||||
@ -420,4 +421,43 @@ describe('component props', () => {
|
|||||||
|
|
||||||
expect(serializeInner(root)).toMatch('<div>60000000100000111</div>')
|
expect(serializeInner(root)).toMatch('<div>60000000100000111</div>')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #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(`<h1>10</h1>`)
|
||||||
|
expect(count).toBe(0)
|
||||||
|
|
||||||
|
bar.value++
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toMatch(`<h1>11</h1>`)
|
||||||
|
expect(count).toBe(0)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -302,7 +302,12 @@ export interface ComponentInternalInstance {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
emitted: Record<string, boolean> | null
|
emitted: Record<string, boolean> | null
|
||||||
|
/**
|
||||||
|
* used for caching the value returned from props default factory functions to
|
||||||
|
* avoid unnecessary watcher trigger
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
propsDefaults: Data
|
||||||
/**
|
/**
|
||||||
* setup related
|
* setup related
|
||||||
* @internal
|
* @internal
|
||||||
@ -440,6 +445,9 @@ export function createComponentInstance(
|
|||||||
emit: null as any, // to be set immediately
|
emit: null as any, // to be set immediately
|
||||||
emitted: null,
|
emitted: null,
|
||||||
|
|
||||||
|
// props default value
|
||||||
|
propsDefaults: EMPTY_OBJ,
|
||||||
|
|
||||||
// state
|
// state
|
||||||
ctx: EMPTY_OBJ,
|
ctx: EMPTY_OBJ,
|
||||||
data: EMPTY_OBJ,
|
data: EMPTY_OBJ,
|
||||||
|
@ -139,6 +139,9 @@ export function initProps(
|
|||||||
const props: Data = {}
|
const props: Data = {}
|
||||||
const attrs: Data = {}
|
const attrs: Data = {}
|
||||||
def(attrs, InternalObjectKey, 1)
|
def(attrs, InternalObjectKey, 1)
|
||||||
|
|
||||||
|
instance.propsDefaults = Object.create(null)
|
||||||
|
|
||||||
setFullProps(instance, rawProps, props, attrs)
|
setFullProps(instance, rawProps, props, attrs)
|
||||||
// validation
|
// validation
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
@ -326,9 +329,14 @@ function resolvePropValue(
|
|||||||
if (hasDefault && value === undefined) {
|
if (hasDefault && value === undefined) {
|
||||||
const defaultValue = opt.default
|
const defaultValue = opt.default
|
||||||
if (opt.type !== Function && isFunction(defaultValue)) {
|
if (opt.type !== Function && isFunction(defaultValue)) {
|
||||||
setCurrentInstance(instance)
|
const { propsDefaults } = instance
|
||||||
value = defaultValue(props)
|
if (key in propsDefaults) {
|
||||||
setCurrentInstance(null)
|
value = propsDefaults[key]
|
||||||
|
} else {
|
||||||
|
setCurrentInstance(instance)
|
||||||
|
value = propsDefaults[key] = defaultValue(props)
|
||||||
|
setCurrentInstance(null)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
value = defaultValue
|
value = defaultValue
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user