diff --git a/packages/runtime-vapor/src/directive.ts b/packages/runtime-vapor/src/directive.ts index 3906eb723..3f63022ae 100644 --- a/packages/runtime-vapor/src/directive.ts +++ b/packages/runtime-vapor/src/directive.ts @@ -27,7 +27,7 @@ export type DirectiveHookName = | 'created' | 'beforeMount' | 'mounted' - // | 'beforeUpdate' + | 'beforeUpdate' | 'updated' | 'beforeUnmount' | 'unmounted' @@ -95,10 +95,21 @@ export function withDirectives( callDirectiveHook(node, binding, 'created') - effect(() => { - if (!instance.isMountedRef.value) return - callDirectiveHook(node, binding, 'updated') - }) + effect( + () => { + if (!instance.isMountedRef.value) return + callDirectiveHook(node, binding, 'beforeUpdate') + }, + { flush: 'pre' }, + ) + + effect( + () => { + if (!instance.isMountedRef.value) return + callDirectiveHook(node, binding, 'updated') + }, + { flush: 'post' }, + ) } return node @@ -129,9 +140,13 @@ function callDirectiveHook( if (!hook) return const newValue = binding.source ? binding.source() : undefined - if (name === 'updated' && binding.value === newValue) return + if ( + ['updated', 'beforeUpdate'].includes(name) && + newValue === binding.oldValue + ) + return - binding.oldValue = binding.value binding.value = newValue hook(node, binding) + if (name !== 'beforeUpdate') binding.oldValue = binding.value } diff --git a/packages/runtime-vapor/src/scheduler.ts b/packages/runtime-vapor/src/scheduler.ts index 876e2c45e..447db74ed 100644 --- a/packages/runtime-vapor/src/scheduler.ts +++ b/packages/runtime-vapor/src/scheduler.ts @@ -2,29 +2,53 @@ import { ReactiveEffect } from '@vue/reactivity' const p = Promise.resolve() -let queued: any[] | undefined +type Queued = { value?: any[] } +let preQueued: Queued = {} +let renderQueued: Queued = {} +let postQueued: Queued = {} -function queue(fn: any) { - if (!queued) { - queued = [fn] +function queue(fn: any, queued: Queued) { + if (!queued.value) { + queued.value = [fn] p.then(flush) } else { - queued.push(fn) + queued.value.push(fn) } } function flush() { - for (let i = 0; i < queued!.length; i++) { - queued![i]() + flushAQueued(preQueued) + flushAQueued(renderQueued) + flushAQueued(postQueued) +} + +function flushAQueued(queued: Queued) { + if (queued.value) { + for (let i = 0; i < queued.value.length; i++) { + queued.value[i]() + } } - queued = undefined + queued.value = undefined } export const nextTick = (fn?: any) => (fn ? p.then(fn) : p) -export function effect(fn: any) { +export type EffectOptions = { + flush?: 'pre' | 'post' | 'render' +} + +export function effect(fn: any, options?: EffectOptions) { let run: () => void - const e = new ReactiveEffect(fn, () => queue(run)) + + const flushMode = options?.flush + const queued = + flushMode === 'pre' + ? preQueued + : flushMode === 'post' + ? postQueued + : renderQueued // default + + const e = new ReactiveEffect(fn, () => queue(run, queued)) run = e.run.bind(e) run() }