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,
|
||||
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('<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
|
||||
*/
|
||||
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
|
||||
* @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,
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user