mirror of https://github.com/vuejs/core.git
fix(defineModel): ensure trigger effect when prop changed (#9841)
close #9838
This commit is contained in:
parent
4070502bd0
commit
eb12f211b8
|
@ -16,7 +16,13 @@ import {
|
||||||
nextTick,
|
nextTick,
|
||||||
ref,
|
ref,
|
||||||
Ref,
|
Ref,
|
||||||
watch
|
watch,
|
||||||
|
openBlock,
|
||||||
|
createVNode,
|
||||||
|
createElementVNode,
|
||||||
|
createBlock,
|
||||||
|
createElementBlock,
|
||||||
|
Fragment
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
import {
|
import {
|
||||||
defineEmits,
|
defineEmits,
|
||||||
|
@ -429,6 +435,84 @@ describe('SFC <script setup> helpers', () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(serializeInner(root)).toBe('2')
|
expect(serializeInner(root)).toBe('2')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #9838
|
||||||
|
test('pass modelValue to slot (optimized mode) ', async () => {
|
||||||
|
let foo: any
|
||||||
|
const update = () => {
|
||||||
|
foo.value = 'bar'
|
||||||
|
}
|
||||||
|
|
||||||
|
const Comp = {
|
||||||
|
render(this: any) {
|
||||||
|
return this.$slots.default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const childRender = vi.fn()
|
||||||
|
const slotRender = vi.fn()
|
||||||
|
const Child = defineComponent({
|
||||||
|
props: ['modelValue'],
|
||||||
|
emits: ['update:modelValue'],
|
||||||
|
setup(props) {
|
||||||
|
foo = useModel(props, 'modelValue')
|
||||||
|
return () => {
|
||||||
|
childRender()
|
||||||
|
return (
|
||||||
|
openBlock(),
|
||||||
|
createElementBlock(Fragment, null, [
|
||||||
|
createVNode(Comp, null, {
|
||||||
|
default: () => {
|
||||||
|
slotRender()
|
||||||
|
return createElementVNode('div', null, foo.value)
|
||||||
|
},
|
||||||
|
_: 1 /* STABLE */
|
||||||
|
})
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const msg = ref('')
|
||||||
|
const setValue = vi.fn(v => (msg.value = v))
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
createApp({
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
openBlock(),
|
||||||
|
createBlock(
|
||||||
|
Child,
|
||||||
|
{
|
||||||
|
modelValue: msg.value,
|
||||||
|
'onUpdate:modelValue': setValue
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
8 /* PROPS */,
|
||||||
|
['modelValue']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}).mount(root)
|
||||||
|
|
||||||
|
expect(foo.value).toBe('')
|
||||||
|
expect(msg.value).toBe('')
|
||||||
|
expect(setValue).not.toBeCalled()
|
||||||
|
expect(childRender).toBeCalledTimes(1)
|
||||||
|
expect(slotRender).toBeCalledTimes(1)
|
||||||
|
expect(serializeInner(root)).toBe('<div></div>')
|
||||||
|
|
||||||
|
// update from child
|
||||||
|
update()
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
expect(msg.value).toBe('bar')
|
||||||
|
expect(foo.value).toBe('bar')
|
||||||
|
expect(setValue).toBeCalledTimes(1)
|
||||||
|
expect(childRender).toBeCalledTimes(2)
|
||||||
|
expect(slotRender).toBeCalledTimes(2)
|
||||||
|
expect(serializeInner(root)).toBe('<div>bar</div>')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('createPropsRestProxy', () => {
|
test('createPropsRestProxy', () => {
|
||||||
|
|
|
@ -364,25 +364,30 @@ export function useModel(props: Record<string, any>, name: string): Ref {
|
||||||
return ref() as any
|
return ref() as any
|
||||||
}
|
}
|
||||||
|
|
||||||
let localValue: any
|
return customRef((track, trigger) => {
|
||||||
watchSyncEffect(() => {
|
let localValue: any
|
||||||
localValue = props[name]
|
watchSyncEffect(() => {
|
||||||
})
|
const propValue = props[name]
|
||||||
|
if (hasChanged(localValue, propValue)) {
|
||||||
return customRef((track, trigger) => ({
|
localValue = propValue
|
||||||
get() {
|
|
||||||
track()
|
|
||||||
return localValue
|
|
||||||
},
|
|
||||||
set(value) {
|
|
||||||
const rawProps = i.vnode!.props
|
|
||||||
if (!(rawProps && name in rawProps) && hasChanged(value, localValue)) {
|
|
||||||
localValue = value
|
|
||||||
trigger()
|
trigger()
|
||||||
}
|
}
|
||||||
i.emit(`update:${name}`, value)
|
})
|
||||||
|
return {
|
||||||
|
get() {
|
||||||
|
track()
|
||||||
|
return localValue
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
const rawProps = i.vnode!.props
|
||||||
|
if (!(rawProps && name in rawProps) && hasChanged(value, localValue)) {
|
||||||
|
localValue = value
|
||||||
|
trigger()
|
||||||
|
}
|
||||||
|
i.emit(`update:${name}`, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function getContext(): SetupContext {
|
function getContext(): SetupContext {
|
||||||
|
|
Loading…
Reference in New Issue