diff --git a/packages/runtime-core/src/compat/renderFn.ts b/packages/runtime-core/src/compat/renderFn.ts index ffbd34e7..ba67cd36 100644 --- a/packages/runtime-core/src/compat/renderFn.ts +++ b/packages/runtime-core/src/compat/renderFn.ts @@ -113,7 +113,7 @@ export function compatH( ): VNode export function compatH( type: string | Component, - props?: LegacyVNodeProps, + props?: Data & LegacyVNodeProps, children?: LegacyVNodeChildren ): VNode @@ -190,16 +190,12 @@ function convertLegacyProps( } else if (key === 'on' || key === 'nativeOn') { const listeners = legacyProps[key] for (const event in listeners) { - const handlerKey = convertLegacyEventKey(event) + let handlerKey = convertLegacyEventKey(event) + if (key === 'nativeOn') handlerKey += `Native` const existing = converted[handlerKey] const incoming = listeners[event] if (existing !== incoming) { if (existing) { - // for the rare case where the same handler is attached - // twice with/without .native modifier... - if (key === 'nativeOn' && String(existing) === String(incoming)) { - continue - } converted[handlerKey] = [].concat(existing as any, incoming as any) } else { converted[handlerKey] = incoming @@ -307,6 +303,7 @@ function convertLegacySlots(vnode: VNode): VNode { } export function defineLegacyVNodeProperties(vnode: VNode) { + /* istanbul ignore if */ if ( isCompatEnabled( DeprecationTypes.RENDER_FUNCTION, diff --git a/packages/vue-compat/__tests__/componentAsync.spec.ts b/packages/vue-compat/__tests__/componentAsync.spec.ts index c7d2a789..9e7316a6 100644 --- a/packages/vue-compat/__tests__/componentAsync.spec.ts +++ b/packages/vue-compat/__tests__/componentAsync.spec.ts @@ -59,4 +59,24 @@ describe('COMPONENT_ASYNC', () => { ) ).toHaveBeenWarned() }) + + test('object syntax', async () => { + const comp = () => ({ + component: Promise.resolve({ template: 'foo' }) + }) + + const vm = new Vue({ + template: `
`, + components: { comp } + }).$mount() + expect(vm.$el.innerHTML).toBe(``) + await timeout(0) + expect(vm.$el.innerHTML).toBe(`foo`) + + expect( + (deprecationData[DeprecationTypes.COMPONENT_ASYNC].message as Function)( + comp + ) + ).toHaveBeenWarned() + }) }) diff --git a/packages/vue-compat/__tests__/global.spec.ts b/packages/vue-compat/__tests__/global.spec.ts index 8b8b066e..ad3619dd 100644 --- a/packages/vue-compat/__tests__/global.spec.ts +++ b/packages/vue-compat/__tests__/global.spec.ts @@ -1,5 +1,6 @@ import Vue from '@vue/compat' import { effect, isReactive } from '@vue/reactivity' +import { nextTick } from '@vue/runtime-core' import { DeprecationTypes, deprecationData, @@ -333,4 +334,50 @@ describe('GLOBAL_PRIVATE_UTIL', () => { deprecationData[DeprecationTypes.GLOBAL_PRIVATE_UTIL].message ).toHaveBeenWarned() }) + + test('defineReactive on instance', async () => { + const vm = new Vue({ + beforeCreate() { + // @ts-ignore + Vue.util.defineReactive(this, 'foo', 1) + }, + template: `
{{ foo }}
` + }).$mount() as any + expect(vm.$el.textContent).toBe('1') + vm.foo = 2 + await nextTick() + expect(vm.$el.textContent).toBe('2') + }) + + test('defineReactive with object value', () => { + const obj: any = {} + const val = { a: 1 } + // @ts-ignore + Vue.util.defineReactive(obj, 'foo', val) + + let n + effect(() => { + n = obj.foo.a + }) + expect(n).toBe(1) + // mutating original + val.a++ + expect(n).toBe(2) + }) + + test('defineReactive with array value', () => { + const obj: any = {} + const val = [1] + // @ts-ignore + Vue.util.defineReactive(obj, 'foo', val) + + let n + effect(() => { + n = obj.foo.length + }) + expect(n).toBe(1) + // mutating original + val.push(2) + expect(n).toBe(2) + }) }) diff --git a/packages/vue-compat/__tests__/renderFn.spec.ts b/packages/vue-compat/__tests__/renderFn.spec.ts index 00ad4c23..27b320e7 100644 --- a/packages/vue-compat/__tests__/renderFn.spec.ts +++ b/packages/vue-compat/__tests__/renderFn.spec.ts @@ -123,9 +123,29 @@ describe('compat: render function', () => { }) ).toMatchObject({ props: { - onClick: fn, // should dedupe + onClick: fn, + onClickNative: fn, onFooBar: fn, - 'onBar-baz': fn + 'onBar-bazNative': fn + } + }) + }) + + test('v2 legacy event prefixes', () => { + const fn = () => {} + expect( + h('div', { + on: { + '&click': fn, + '~keyup': fn, + '!touchend': fn + } + }) + ).toMatchObject({ + props: { + onClickPassive: fn, + onKeyupOnce: fn, + onTouchendCapture: fn } }) })