diff --git a/packages-private/vapor-e2e-test/todomvc/App.vue b/packages-private/vapor-e2e-test/todomvc/App.vue index e7f97968b..910ada510 100644 --- a/packages-private/vapor-e2e-test/todomvc/App.vue +++ b/packages-private/vapor-e2e-test/todomvc/App.vue @@ -2,11 +2,9 @@ import { reactive, computed, - watchEffect, onMounted, onUnmounted, - next, - nextTick, + watchPostEffect, } from 'vue' const STORAGE_KEY = 'todos-vuejs-3.x' @@ -72,7 +70,7 @@ const state = reactive({ }), }) -watchEffect(() => { +watchPostEffect(() => { todoStorage.save(state.todos) }) @@ -138,8 +136,8 @@ function removeCompleted() { } // vapor custom directive -const vTodoFocus = (el, value) => () => { - if (value()) nextTick(() => el.focus()) +const vTodoFocus = (el, value) => { + watchPostEffect(() => value() && el.focus()) } diff --git a/packages/runtime-vapor/__tests__/directives/customDirective.spec.ts b/packages/runtime-vapor/__tests__/directives/customDirective.spec.ts new file mode 100644 index 000000000..1fd5f95ee --- /dev/null +++ b/packages/runtime-vapor/__tests__/directives/customDirective.spec.ts @@ -0,0 +1,39 @@ +import { effectScope, ref } from '@vue/reactivity' +import { type VaporDirective, withVaporDirectives } from '../../src' +import { nextTick, watchEffect } from '@vue/runtime-dom' + +describe('custom directive', () => { + it('should work', async () => { + const teardown = vi.fn() + const dir: VaporDirective = vi.fn((el, source) => { + watchEffect(() => { + el.textContent = source() + }) + return teardown + }) + const scope = effectScope() + const el = document.createElement('div') + const n = ref(1) + const source = () => n.value + const modifiers = { mod: true } + scope.run(() => { + withVaporDirectives(el, [[dir, source, undefined, modifiers]]) + }) + expect(dir).toHaveBeenCalledWith(el, source, undefined, modifiers) + expect(teardown).not.toHaveBeenCalled() + + expect(el.textContent).toBe('1') + + n.value = 2 + await nextTick() + expect(el.textContent).toBe('2') + + scope.stop() + expect(teardown).toHaveBeenCalled() + + n.value = 3 + await nextTick() + // should be stopped and not update + expect(el.textContent).toBe('2') + }) +}) diff --git a/packages/runtime-vapor/src/directives/custom.ts b/packages/runtime-vapor/src/directives/custom.ts index 008caa746..32cfe968b 100644 --- a/packages/runtime-vapor/src/directives/custom.ts +++ b/packages/runtime-vapor/src/directives/custom.ts @@ -1,6 +1,5 @@ -import type { DirectiveModifiers } from '@vue/runtime-dom' +import { type DirectiveModifiers, onScopeDispose } from '@vue/runtime-dom' import type { VaporComponentInstance } from '../component' -import { renderEffect } from '../renderEffect' // !! vapor directive is different from vdom directives export type VaporDirective = ( @@ -13,11 +12,11 @@ export type VaporDirective = ( type VaporDirectiveArguments = Array< | [VaporDirective | undefined] | [VaporDirective | undefined, () => any] - | [VaporDirective | undefined, () => any, argument: string] + | [VaporDirective | undefined, (() => any) | undefined, argument: string] | [ VaporDirective | undefined, - value: () => any, - argument: string, + value: (() => any) | undefined, + argument: string | undefined, modifiers: DirectiveModifiers, ] > @@ -30,7 +29,7 @@ export function withVaporDirectives( for (const [dir, value, argument, modifiers] of dirs) { if (dir) { const ret = dir(node, value, argument, modifiers) - if (ret) renderEffect(ret) + if (ret) onScopeDispose(ret) } } }