fix(runtime-core): cloneVNode should preserve correct ctx instance when normalizing ref

fix #1311
This commit is contained in:
Evan You 2020-06-15 15:57:37 -04:00
parent 605953a154
commit be69beed5e
2 changed files with 48 additions and 15 deletions

View File

@ -14,6 +14,7 @@ import { Data } from '../src/component'
import { ShapeFlags, PatchFlags } from '@vue/shared' import { ShapeFlags, PatchFlags } from '@vue/shared'
import { h, reactive, isReactive } from '../src' import { h, reactive, isReactive } from '../src'
import { createApp, nodeOps, serializeInner } from '@vue/runtime-test' import { createApp, nodeOps, serializeInner } from '@vue/runtime-test'
import { setCurrentRenderingInstance } from '../src/componentRenderUtils'
describe('vnode', () => { describe('vnode', () => {
test('create with just tag', () => { test('create with just tag', () => {
@ -198,24 +199,55 @@ describe('vnode', () => {
expect(cloned2).toEqual(node2) expect(cloned2).toEqual(node2)
expect(cloneVNode(node2)).toEqual(node2) expect(cloneVNode(node2)).toEqual(node2)
expect(cloneVNode(node2)).toEqual(cloned2) expect(cloneVNode(node2)).toEqual(cloned2)
})
test('cloneVNode key normalization', () => {
// #1041 should use resolved key/ref // #1041 should use resolved key/ref
expect(cloneVNode(createVNode('div', { key: 1 })).key).toBe(1) expect(cloneVNode(createVNode('div', { key: 1 })).key).toBe(1)
expect(cloneVNode(createVNode('div', { key: 1 }), { key: 2 }).key).toBe(2) expect(cloneVNode(createVNode('div', { key: 1 }), { key: 2 }).key).toBe(2)
expect(cloneVNode(createVNode('div'), { key: 2 }).key).toBe(2) expect(cloneVNode(createVNode('div'), { key: 2 }).key).toBe(2)
})
// ref normalizes to [currentRenderingInstance, ref] // ref normalizes to [currentRenderingInstance, ref]
expect(cloneVNode(createVNode('div', { ref: 'foo' })).ref).toEqual([ test('cloneVNode ref normalization', () => {
null, const mockInstance1 = {} as any
'foo' const mockInstance2 = {} as any
])
expect( setCurrentRenderingInstance(mockInstance1)
cloneVNode(createVNode('div', { ref: 'foo' }), { ref: 'bar' }).ref const original = createVNode('div', { ref: 'foo' })
).toEqual([null, 'bar']) expect(original.ref).toEqual([mockInstance1, 'foo'])
expect(cloneVNode(createVNode('div'), { ref: 'bar' }).ref).toEqual([
null, // clone and preserve original ref
'bar' const cloned1 = cloneVNode(original)
]) expect(cloned1.ref).toEqual([mockInstance1, 'foo'])
// cloning with new ref, but with same context instance
const cloned2 = cloneVNode(original, { ref: 'bar' })
expect(cloned2.ref).toEqual([mockInstance1, 'bar'])
// cloning and adding ref to original that has no ref
const original2 = createVNode('div')
const cloned3 = cloneVNode(original2, { ref: 'bar' })
expect(cloned3.ref).toEqual([mockInstance1, 'bar'])
// cloning with different context instance
setCurrentRenderingInstance(mockInstance2)
// clone and preserve original ref
const cloned4 = cloneVNode(original)
// #1311 should preserve original context instance!
expect(cloned4.ref).toEqual([mockInstance1, 'foo'])
// cloning with new ref, but with same context instance
const cloned5 = cloneVNode(original, { ref: 'bar' })
// new ref should use current context instance and overwrite orgiinal
expect(cloned5.ref).toEqual([mockInstance2, 'bar'])
// cloning and adding ref to original that has no ref
const cloned6 = cloneVNode(original2, { ref: 'bar' })
expect(cloned6.ref).toEqual([mockInstance2, 'bar'])
setCurrentRenderingInstance(null)
}) })
describe('mergeProps', () => { describe('mergeProps', () => {

View File

@ -277,12 +277,13 @@ export const InternalObjectKey = `__vInternal`
const normalizeKey = ({ key }: VNodeProps): VNode['key'] => const normalizeKey = ({ key }: VNodeProps): VNode['key'] =>
key != null ? key : null key != null ? key : null
const normalizeRef = ({ ref }: VNodeProps): VNode['ref'] => const normalizeRef = ({ ref }: VNodeProps): VNode['ref'] => {
(ref != null return (ref != null
? isArray(ref) ? isArray(ref)
? ref ? ref
: [currentRenderingInstance!, ref] : [currentRenderingInstance!, ref]
: null) as any : null) as any
}
export const createVNode = (__DEV__ export const createVNode = (__DEV__
? createVNodeWithArgsTransform ? createVNodeWithArgsTransform
@ -420,7 +421,7 @@ export function cloneVNode<T, U>(
type: vnode.type, type: vnode.type,
props, props,
key: props && normalizeKey(props), key: props && normalizeKey(props),
ref: props && normalizeRef(props), ref: extraProps && extraProps.ref ? normalizeRef(extraProps) : vnode.ref,
scopeId: vnode.scopeId, scopeId: vnode.scopeId,
children: vnode.children, children: vnode.children,
target: vnode.target, target: vnode.target,