diff --git a/benchmark/tsconfig.json b/benchmark/tsconfig.json index 0a2e412b2..c769cb57d 100644 --- a/benchmark/tsconfig.json +++ b/benchmark/tsconfig.json @@ -19,6 +19,7 @@ "paths": { "vue": ["../packages/vue/src"], "@vue/vapor": ["../packages/vue-vapor/src"], + "vue/vapor": ["../packages/vue-vapor/src"], "@vue/*": ["../packages/*/src"] } }, diff --git a/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts b/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts index 872222c7f..0451d2650 100644 --- a/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts +++ b/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts @@ -153,7 +153,7 @@ describe('api: createVaporApp', () => { expect(host.innerHTML).toBe(`foobar!barbaz!`) }) - test('directive', () => { + test.todo('directive', () => { const spy1 = vi.fn() const spy2 = vi.fn() diff --git a/packages/runtime-vapor/__tests__/directives.spec.ts b/packages/runtime-vapor/__tests__/directives.spec.ts index 4d46daa35..cfb882bcd 100644 --- a/packages/runtime-vapor/__tests__/directives.spec.ts +++ b/packages/runtime-vapor/__tests__/directives.spec.ts @@ -19,7 +19,7 @@ import { makeRender } from './_utils' const define = makeRender() -describe('directives', () => { +describe.todo('directives', () => { it('should work', async () => { const count = ref(0) diff --git a/packages/runtime-vapor/__tests__/directives/directives.spec.ts b/packages/runtime-vapor/__tests__/directives/directives.spec.ts index 881d80dc5..211d3b741 100644 --- a/packages/runtime-vapor/__tests__/directives/directives.spec.ts +++ b/packages/runtime-vapor/__tests__/directives/directives.spec.ts @@ -15,7 +15,7 @@ import { makeRender } from '../_utils' const define = makeRender() -describe('directives', () => { +describe.todo('directives', () => { it('should work', async () => { const count = ref(0) diff --git a/packages/runtime-vapor/__tests__/directives/vModel.spec.ts b/packages/runtime-vapor/__tests__/directives/vModel.spec.ts index 7d13388d1..8ee22607f 100644 --- a/packages/runtime-vapor/__tests__/directives/vModel.spec.ts +++ b/packages/runtime-vapor/__tests__/directives/vModel.spec.ts @@ -27,7 +27,7 @@ const setDOMProps = (el: any, props: Array<[key: string, value: any]>) => { }) } -describe('directive: v-model', () => { +describe.todo('directive: v-model', () => { test('should work with text input', async () => { const spy = vi.fn() diff --git a/packages/runtime-vapor/__tests__/directives/vShow.spec.ts b/packages/runtime-vapor/__tests__/directives/vShow.spec.ts index 59777e6fc..702ea1be0 100644 --- a/packages/runtime-vapor/__tests__/directives/vShow.spec.ts +++ b/packages/runtime-vapor/__tests__/directives/vShow.spec.ts @@ -28,7 +28,7 @@ const createDemo = (defaultValue: boolean) => on(n1 as HTMLElement, 'click', () => handleClick) return n0 }) -describe('directive: v-show', () => { +describe.todo('directive: v-show', () => { test('basic', async () => { const { host } = createDemo(true).render() const btn = host.querySelector('button') diff --git a/packages/runtime-vapor/__tests__/for.spec.ts b/packages/runtime-vapor/__tests__/for.spec.ts index 8cbbbedb6..9ce3ffa17 100644 --- a/packages/runtime-vapor/__tests__/for.spec.ts +++ b/packages/runtime-vapor/__tests__/for.spec.ts @@ -194,7 +194,7 @@ describe('createFor', () => { expect(host.innerHTML).toBe('') }) - test('should work with directive hooks', async () => { + test.fails('should work with directive hooks', async () => { const calls: string[] = [] const list = ref([0]) const update = ref(0) diff --git a/packages/runtime-vapor/__tests__/if.spec.ts b/packages/runtime-vapor/__tests__/if.spec.ts index f8bdb4f52..492d19d61 100644 --- a/packages/runtime-vapor/__tests__/if.spec.ts +++ b/packages/runtime-vapor/__tests__/if.spec.ts @@ -134,7 +134,7 @@ describe('createIf', () => { expect(host.innerHTML).toBe('') }) - test('should work with directive hooks', async () => { + test.todo('should work with directive hooks', async () => { const calls: string[] = [] const show1 = ref(true) const show2 = ref(true) diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts index 07bbd151f..ee56eabf3 100644 --- a/packages/runtime-vapor/src/apiCreateFor.ts +++ b/packages/runtime-vapor/src/apiCreateFor.ts @@ -1,10 +1,10 @@ import { + type EffectScope, type ShallowRef, - getCurrentScope, + effectScope, isReactive, proxyRefs, shallowRef, - traverse, triggerRef, } from '@vue/reactivity' import { isArray, isObject, isString } from '@vue/shared' @@ -18,17 +18,11 @@ import { type Block, type Fragment, fragmentKey } from './apiRender' import { warn } from './warning' import { currentInstance } from './component' import { componentKey } from './component' -import { BlockEffectScope, isRenderEffectScope } from './blockEffectScope' -import { - createChildFragmentDirectives, - invokeWithMount, - invokeWithUnmount, - invokeWithUpdate, -} from './directivesChildFragment' import type { DynamicSlot } from './componentSlots' +import { renderEffect } from './renderEffect' interface ForBlock extends Fragment { - scope: BlockEffectScope + scope: EffectScope state: [ item: ShallowRef, key: ShallowRef, @@ -53,8 +47,6 @@ export const createFor = ( let oldBlocks: ForBlock[] = [] let newBlocks: ForBlock[] let parent: ParentNode | undefined | null - const update = getMemo ? updateWithMemo : updateWithoutMemo - const parentScope = getCurrentScope()! const parentAnchor = __DEV__ ? createComment('for') : createTextNode() const ref: Fragment = { nodes: oldBlocks, @@ -62,25 +54,17 @@ export const createFor = ( } const instance = currentInstance! - if (__DEV__ && (!instance || !isRenderEffectScope(parentScope))) { + if (__DEV__ && !instance) { warn('createFor() can only be used inside setup()') } - createChildFragmentDirectives( - parentAnchor, - () => oldBlocks.map(b => b.scope), - // source getter - () => traverse(src(), 1), - // init cb - getValue => doFor(getValue()), - // effect cb - getValue => doFor(getValue()), - once, - ) + const update = getMemo ? updateWithMemo : updateWithoutMemo + once ? renderList() : renderEffect(renderList) return ref - function doFor(source: any) { + function renderList() { + const source = src() const newLength = getLength(source) const oldLength = oldBlocks.length newBlocks = new Array(newLength) @@ -265,7 +249,7 @@ export const createFor = ( idx: number, anchor: Node = parentAnchor, ): ForBlock { - const scope = new BlockEffectScope(instance, parentScope) + const scope = effectScope() const [item, key, index] = getItem(source, idx) const state = [ @@ -283,11 +267,9 @@ export const createFor = ( }) block.nodes = scope.run(() => renderItem(proxyRefs(state)))! - invokeWithMount(scope, () => { - // TODO v-memo - // if (getMemo) block.update() - if (parent) insert(block.nodes, parent, anchor) - }) + // TODO v-memo + // if (getMemo) block.update() + if (parent) insert(block.nodes, parent, anchor) return block } @@ -326,7 +308,6 @@ export const createFor = ( } if (needsUpdate) setState(block, newItem, newKey, newIndex) - invokeWithUpdate(block.scope) } function updateWithoutMemo( @@ -344,13 +325,11 @@ export const createFor = ( (!isReactive(newItem) && isObject(newItem)) if (needsUpdate) setState(block, newItem, newKey, newIndex) - invokeWithUpdate(block.scope) } function unmount({ nodes, scope }: ForBlock) { - invokeWithUnmount(scope, () => { - removeBlock(nodes, parent!) - }) + removeBlock(nodes, parent!) + scope.stop() } } diff --git a/packages/runtime-vapor/src/apiCreateIf.ts b/packages/runtime-vapor/src/apiCreateIf.ts index dce38bae9..b3e5799e2 100644 --- a/packages/runtime-vapor/src/apiCreateIf.ts +++ b/packages/runtime-vapor/src/apiCreateIf.ts @@ -1,15 +1,7 @@ +import { renderEffect } from './renderEffect' import { type Block, type Fragment, fragmentKey } from './apiRender' -import { getCurrentScope } from '@vue/reactivity' +import { type EffectScope, effectScope } from '@vue/reactivity' import { createComment, createTextNode, insert, remove } from './dom/element' -import { currentInstance } from './component' -import { warn } from './warning' -import { BlockEffectScope, isRenderEffectScope } from './blockEffectScope' -import { - createChildFragmentDirectives, - invokeWithMount, - invokeWithUnmount, - invokeWithUpdate, -} from './directivesChildFragment' type BlockFn = () => Block @@ -26,8 +18,7 @@ export const createIf = ( let branch: BlockFn | undefined let parent: ParentNode | undefined | null let block: Block | undefined - let scope: BlockEffectScope | undefined - const parentScope = getCurrentScope()! + let scope: EffectScope | undefined const anchor = __DEV__ ? createComment('if') : createTextNode() const fragment: Fragment = { nodes: [], @@ -35,37 +26,17 @@ export const createIf = ( [fragmentKey]: true, } - const instance = currentInstance! - if (__DEV__ && (!instance || !isRenderEffectScope(parentScope))) { - warn('createIf() can only be used inside setup()') - } - // TODO: SSR // if (isHydrating) { // parent = hydrationNode!.parentNode // setCurrentHydrationNode(hydrationNode!) // } - createChildFragmentDirectives( - anchor, - () => (scope ? [scope] : []), - // source getter - condition, - // init cb - getValue => { - newValue = !!getValue() - doIf() - }, - // effect cb - getValue => { - if ((newValue = !!getValue()) !== oldValue) { - doIf() - } else if (scope) { - invokeWithUpdate(scope) - } - }, - once, - ) + if (once) { + doIf() + } else { + renderEffect(() => doIf()) + } // TODO: SSR // if (isHydrating) { @@ -75,17 +46,20 @@ export const createIf = ( return fragment function doIf() { - parent ||= anchor.parentNode - if (block) { - invokeWithUnmount(scope!, () => remove(block!, parent!)) - } - if ((branch = (oldValue = newValue) ? b1 : b2)) { - scope = new BlockEffectScope(instance, parentScope) - fragment.nodes = block = scope.run(branch)! - invokeWithMount(scope, () => parent && insert(block!, parent, anchor)) - } else { - scope = block = undefined - fragment.nodes = [] + if ((newValue = !!condition()) !== oldValue) { + parent ||= anchor.parentNode + if (block) { + scope!.stop() + remove(block, parent!) + } + if ((branch = (oldValue = newValue) ? b1 : b2)) { + scope = effectScope() + fragment.nodes = block = scope.run(branch)! + parent && insert(block, parent, anchor) + } else { + scope = block = undefined + fragment.nodes = [] + } } } } diff --git a/packages/runtime-vapor/src/blockEffectScope.ts b/packages/runtime-vapor/src/blockEffectScope.ts deleted file mode 100644 index 92fc01983..000000000 --- a/packages/runtime-vapor/src/blockEffectScope.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { EffectScope } from '@vue/reactivity' -import type { ComponentInternalInstance } from './component' -import type { DirectiveBindingsMap } from './directives' - -export class BlockEffectScope extends EffectScope { - /** - * instance - * @internal - */ - it: ComponentInternalInstance - /** - * isMounted - * @internal - */ - im: boolean - /** - * directives - * @internal - */ - dirs?: DirectiveBindingsMap - - constructor( - instance: ComponentInternalInstance, - parentScope: EffectScope | null, - ) { - super(false, parentScope || undefined) - this.im = false - this.it = instance - } -} - -export function isRenderEffectScope( - scope: EffectScope | undefined, -): scope is BlockEffectScope { - return scope instanceof BlockEffectScope -} diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 7f4100ec3..0c9901da0 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -1,4 +1,4 @@ -import { isRef } from '@vue/reactivity' +import { EffectScope, isRef } from '@vue/reactivity' import { EMPTY_OBJ, hasOwn, @@ -31,7 +31,6 @@ import { createAppContext, } from './apiCreateVaporApp' import type { Data } from '@vue/runtime-shared' -import { BlockEffectScope } from './blockEffectScope' export type Component = FunctionalComponent | ObjectComponent @@ -161,7 +160,7 @@ export interface ComponentInternalInstance { root: ComponentInternalInstance provides: Data - scope: BlockEffectScope + scope: EffectScope comps: Set rawProps: NormalizedRawProps @@ -283,7 +282,7 @@ export function createComponentInstance( parent, root: null!, // set later - scope: null!, + scope: new EffectScope(true /* detached */)!, provides: parent ? parent.provides : Object.create(_appContext.provides), type: component, comps: new Set(), @@ -358,7 +357,6 @@ export function createComponentInstance( // [VaporLifecycleHooks.SERVER_PREFETCH]: null, } instance.root = parent ? parent.root : instance - instance.scope = new BlockEffectScope(instance, parent && parent.scope) initProps(instance, rawProps, !isFunction(component), once) initSlots(instance, slots) instance.emit = emit.bind(null, instance) diff --git a/packages/runtime-vapor/src/componentLifecycle.ts b/packages/runtime-vapor/src/componentLifecycle.ts index 3341e9e89..41e9edb2e 100644 --- a/packages/runtime-vapor/src/componentLifecycle.ts +++ b/packages/runtime-vapor/src/componentLifecycle.ts @@ -2,7 +2,7 @@ import { invokeArrayFns } from '@vue/shared' import type { VaporLifecycleHooks } from './enums' import { type ComponentInternalInstance, setCurrentInstance } from './component' import { queuePostFlushCb } from './scheduler' -import { type DirectiveHookName, invokeDirectiveHook } from './directives' +import type { DirectiveHookName } from './directives' export function invokeLifecycle( instance: ComponentInternalInstance, @@ -24,8 +24,6 @@ export function invokeLifecycle( } post ? queuePostFlushCb(fn) : fn() } - - invokeDirectiveHook(instance, directive, instance.scope) } function invokeSub() { diff --git a/packages/runtime-vapor/src/directives.ts b/packages/runtime-vapor/src/directives.ts index ae0de07ee..b6a09c322 100644 --- a/packages/runtime-vapor/src/directives.ts +++ b/packages/runtime-vapor/src/directives.ts @@ -1,27 +1,6 @@ -import { invokeArrayFns, isBuiltInDirective, isFunction } from '@vue/shared' -import { - type ComponentInternalInstance, - currentInstance, - isVaporComponent, - setCurrentInstance, -} from './component' -import { - EffectFlags, - ReactiveEffect, - getCurrentScope, - pauseTracking, - resetTracking, - traverse, -} from '@vue/reactivity' -import { - VaporErrorCodes, - callWithAsyncErrorHandling, - callWithErrorHandling, -} from './errorHandling' -import { type SchedulerJob, queueJob, queuePostFlushCb } from './scheduler' +import { isBuiltInDirective } from '@vue/shared' +import { type ComponentInternalInstance, currentInstance } from './component' import { warn } from './warning' -import { type BlockEffectScope, isRenderEffectScope } from './blockEffectScope' -import { normalizeBlock } from './dom/element' export type DirectiveModifiers = Record @@ -98,168 +77,7 @@ export function withDirectives( return nodeOrComponent } - let node: Node - if (isVaporComponent(nodeOrComponent)) { - const root = getComponentNode(nodeOrComponent) - if (!root) return nodeOrComponent - node = root - } else { - node = nodeOrComponent - } - - let bindings: DirectiveBinding[] - const instance = currentInstance! - const parentScope = getCurrentScope() as BlockEffectScope - - if (__DEV__ && !isRenderEffectScope(parentScope)) { - warn(`Directives should be used inside of RenderEffectScope.`) - } - - const directivesMap = (parentScope.dirs ||= new Map()) - if (!(bindings = directivesMap.get(node))) { - directivesMap.set(node, (bindings = [])) - } - - for (const directive of directives) { - let [dir, source, arg, modifiers] = directive - if (!dir) continue - if (isFunction(dir)) { - dir = { - mounted: dir, - updated: dir, - } satisfies ObjectDirective - } - - const binding: DirectiveBinding = { - dir, - instance, - value: null, // set later - oldValue: undefined, - arg, - modifiers, - } - - if (source) { - if (dir.deep) { - const deep = dir.deep === true ? undefined : dir.deep - const baseSource = source - source = () => traverse(baseSource(), deep) - } - - const effect = new ReactiveEffect(() => - callWithErrorHandling( - source!, - instance, - VaporErrorCodes.RENDER_FUNCTION, - ), - ) - const triggerRenderingUpdate = createRenderingUpdateTrigger( - instance, - effect, - ) - effect.scheduler = () => queueJob(triggerRenderingUpdate) - - binding.source = effect.run.bind(effect) - } - - bindings.push(binding) - - callDirectiveHook(node, binding, instance, 'created') - } + // NOOP return nodeOrComponent } - -function getComponentNode(component: ComponentInternalInstance) { - if (!component.block) return - - const nodes = normalizeBlock(component.block) - if (nodes.length !== 1) { - warn( - `Runtime directive used on component with non-element root node. ` + - `The directives will not function as intended.`, - ) - return - } - - return nodes[0] -} - -export function invokeDirectiveHook( - instance: ComponentInternalInstance | null, - name: DirectiveHookName, - scope: BlockEffectScope, -): void { - const { dirs } = scope - if (name === 'mounted') scope.im = true - if (!dirs) return - const iterator = dirs.entries() - for (const [node, bindings] of iterator) { - for (const binding of bindings) { - callDirectiveHook(node, binding, instance, name) - } - } -} - -function callDirectiveHook( - node: Node, - binding: DirectiveBinding, - instance: ComponentInternalInstance | null, - name: DirectiveHookName, -) { - if (name === 'beforeUpdate') binding.oldValue = binding.value - const { dir } = binding - const hook = dir[name] - if (!hook) return - - const newValue = binding.source ? binding.source() : undefined - binding.value = newValue - // disable tracking inside all lifecycle hooks - // since they can potentially be called inside effects. - pauseTracking() - callWithAsyncErrorHandling(hook, instance, VaporErrorCodes.DIRECTIVE_HOOK, [ - node, - binding, - ]) - resetTracking() -} - -export function createRenderingUpdateTrigger( - instance: ComponentInternalInstance, - effect: ReactiveEffect, -): SchedulerJob { - job.id = instance.uid - return job - function job() { - if (!(effect.flags & EffectFlags.ACTIVE) || !effect.dirty) { - return - } - - if (instance.isMounted && !instance.isUpdating) { - instance.isUpdating = true - const reset = setCurrentInstance(instance) - - const { bu, u, scope } = instance - const { dirs } = scope - // beforeUpdate hook - if (bu) { - invokeArrayFns(bu) - } - invokeDirectiveHook(instance, 'beforeUpdate', scope) - - queuePostFlushCb(() => { - instance.isUpdating = false - const reset = setCurrentInstance(instance) - if (dirs) { - invokeDirectiveHook(instance, 'updated', scope) - } - // updated hook - if (u) { - queuePostFlushCb(u) - } - reset() - }) - reset() - } - } -} diff --git a/packages/runtime-vapor/src/directivesChildFragment.ts b/packages/runtime-vapor/src/directivesChildFragment.ts deleted file mode 100644 index 8965002ac..000000000 --- a/packages/runtime-vapor/src/directivesChildFragment.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { ReactiveEffect, getCurrentScope } from '@vue/reactivity' -import { - type Directive, - type DirectiveHookName, - createRenderingUpdateTrigger, - invokeDirectiveHook, -} from './directives' -import { warn } from './warning' -import { type BlockEffectScope, isRenderEffectScope } from './blockEffectScope' -import { currentInstance } from './component' -import { VaporErrorCodes, callWithErrorHandling } from './errorHandling' -import { queueJob, queuePostFlushCb } from './scheduler' - -/** - * used in createIf and createFor - * manage directives of child fragments in components. - */ -export function createChildFragmentDirectives( - anchor: Node, - getScopes: () => BlockEffectScope[], - source: () => any, - initCallback: (getValue: () => any) => void, - effectCallback: (getValue: () => any) => void, - once?: boolean, -): void { - let isTriggered = false - const instance = currentInstance! - const parentScope = getCurrentScope() as BlockEffectScope - if (__DEV__) { - if (!isRenderEffectScope(parentScope)) { - warn('child directives can only be added to a render effect scope') - } - if (!instance) { - warn('child directives can only be added in a component') - } - } - - const callSourceWithErrorHandling = () => - callWithErrorHandling(source, instance, VaporErrorCodes.RENDER_FUNCTION) - - if (once) { - initCallback(callSourceWithErrorHandling) - return - } - - const directiveBindingsMap = (parentScope.dirs ||= new Map()) - const dir: Directive = { - beforeUpdate: onDirectiveBeforeUpdate, - beforeMount: () => invokeChildrenDirectives('beforeMount'), - mounted: () => invokeChildrenDirectives('mounted'), - beforeUnmount: () => invokeChildrenDirectives('beforeUnmount'), - unmounted: () => invokeChildrenDirectives('unmounted'), - } - directiveBindingsMap.set(anchor, [ - { - dir, - instance, - value: null, - oldValue: undefined, - }, - ]) - - const effect = new ReactiveEffect(callSourceWithErrorHandling) - const triggerRenderingUpdate = createRenderingUpdateTrigger(instance, effect) - effect.scheduler = () => { - isTriggered = true - queueJob(triggerRenderingUpdate) - } - - const getValue = () => effect.run() - - initCallback(getValue) - - function onDirectiveBeforeUpdate() { - if (isTriggered) { - isTriggered = false - effectCallback(getValue) - } else { - const scopes = getScopes() - for (const scope of scopes) { - invokeWithUpdate(scope) - } - return - } - } - - function invokeChildrenDirectives(name: DirectiveHookName) { - const scopes = getScopes() - for (const scope of scopes) { - invokeDirectiveHook(instance, name, scope) - } - } -} - -export function invokeWithMount( - scope: BlockEffectScope, - handler?: () => any, -): any { - if (isRenderEffectScope(scope.parent) && !scope.parent.im) { - return handler && handler() - } - return invokeWithDirsHooks(scope, 'mount', handler) -} - -export function invokeWithUnmount( - scope: BlockEffectScope, - handler?: () => void, -): any { - try { - return invokeWithDirsHooks(scope, 'unmount', handler) - } finally { - scope.stop() - } -} - -export function invokeWithUpdate( - scope: BlockEffectScope, - handler?: () => void, -): any { - return invokeWithDirsHooks(scope, 'update', handler) -} - -const lifecycleMap = { - mount: ['beforeMount', 'mounted'], - update: ['beforeUpdate', 'updated'], - unmount: ['beforeUnmount', 'unmounted'], -} as const - -function invokeWithDirsHooks( - scope: BlockEffectScope, - name: keyof typeof lifecycleMap, - handler?: () => any, -) { - const { dirs, it: instance } = scope - const [before, after] = lifecycleMap[name] - - if (!dirs) { - const res = handler && handler() - if (name === 'mount') { - queuePostFlushCb(() => (scope.im = true)) - } - return res - } - - invokeDirectiveHook(instance, before, scope) - try { - if (handler) { - return handler() - } - } finally { - queuePostFlushCb(() => { - invokeDirectiveHook(instance, after, scope) - }) - } -} diff --git a/packages/runtime-vapor/src/renderEffect.ts b/packages/runtime-vapor/src/renderEffect.ts index e95e9df30..807145681 100644 --- a/packages/runtime-vapor/src/renderEffect.ts +++ b/packages/runtime-vapor/src/renderEffect.ts @@ -12,7 +12,6 @@ import { queuePostFlushCb, } from './scheduler' import { VaporErrorCodes, callWithAsyncErrorHandling } from './errorHandling' -import { invokeDirectiveHook } from './directives' export function renderEffect(cb: () => void): void { const instance = getCurrentInstance() @@ -58,24 +57,17 @@ export function renderEffect(cb: () => void): void { if (instance && instance.isMounted && !instance.isUpdating) { instance.isUpdating = true - const { bu, u, scope } = instance - const { dirs } = scope + const { bu, u } = instance // beforeUpdate hook if (bu) { invokeArrayFns(bu) } - if (dirs) { - invokeDirectiveHook(instance, 'beforeUpdate', scope) - } effect.run() queuePostFlushCb(() => { instance.isUpdating = false const reset = setCurrentInstance(instance) - if (dirs) { - invokeDirectiveHook(instance, 'updated', scope) - } // updated hook if (u) { queuePostFlushCb(u)