From: 三咲智子 Kevin Deng Date: Mon, 4 Dec 2023 08:08:15 +0000 (+0800) Subject: feat: support more directive hook X-Git-Tag: v3.6.0-alpha.1~16^2~755 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f3e80d7706a531600c9e2ba760439e0917ce6527;p=thirdparty%2Fvuejs%2Fcore.git feat: support more directive hook --- diff --git a/packages/compiler-vapor/src/generate.ts b/packages/compiler-vapor/src/generate.ts index 0b84f86387..4cd284e84a 100644 --- a/packages/compiler-vapor/src/generate.ts +++ b/packages/compiler-vapor/src/generate.ts @@ -19,9 +19,10 @@ import { VaporHelper, IRExpression, SetEventIRNode, + WithDirectiveIRNode, } from './ir' import { SourceMapGenerator } from 'source-map-js' -import { camelize, capitalize, isString } from '@vue/shared' +import { camelize, isString } from '@vue/shared' // remove when stable // @ts-expect-error @@ -249,9 +250,17 @@ export function generate( ) } + for (const oper of ir.operation.filter( + (oper): oper is WithDirectiveIRNode => + oper.type === IRNodeTypes.WITH_DIRECTIVE, + )) { + genWithDirective(oper, ctx) + } + for (const operation of ir.operation) { genOperation(operation, ctx) } + for (const { operations } of ir.effect) { pushWithNewline(`${vaporHelper('effect')}(() => {`) indent() @@ -261,6 +270,7 @@ export function generate( deindent() pushWithNewline('})') } + // TODO multiple-template // TODO return statement in IR pushWithNewline(`return n${ir.dynamic.id}`) @@ -363,20 +373,7 @@ function genOperation(oper: OperationNode, context: CodegenContext) { return } case IRNodeTypes.WITH_DIRECTIVE: { - // TODO merge directive for the same node - pushWithNewline(`${vaporHelper('withDirectives')}(n${oper.element}, [[`) - - // TODO resolve directive - const directiveReference = camelize(`v-${oper.name}`) - if (context.bindingMetadata[directiveReference]) { - genExpression(createSimpleExpression(directiveReference), context) - } - - if (oper.binding) { - push(', ') - genExpression(oper.binding, context) - } - push(']])') + // generated, skip return } default: @@ -483,3 +480,23 @@ function genSetEvent(oper: SetEventIRNode, context: CodegenContext) { push(')') } + +function genWithDirective(oper: WithDirectiveIRNode, context: CodegenContext) { + const { push, pushWithNewline, vaporHelper, bindingMetadata } = context + + // TODO merge directive for the same node + pushWithNewline(`${vaporHelper('withDirectives')}(n${oper.element}, [[`) + + // TODO resolve directive + const directiveReference = camelize(`v-${oper.name}`) + if (bindingMetadata[directiveReference]) { + genExpression(createSimpleExpression(directiveReference), context) + } + + if (oper.binding) { + push(', ') + genExpression(oper.binding, context) + } + push(']])') + return +} diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index c6195f36ef..ead75c9328 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -24,16 +24,12 @@ export const getCurrentInstance: () => ComponentInternalInstance | null = () => export const setCurrentInstance = (instance: ComponentInternalInstance) => { currentInstance = instance - instance.scope.on() } export const unsetCurrentInstance = () => { - currentInstance && currentInstance.scope.off() currentInstance = null } -export interface ComponentPublicInstance {} - let uid = 0 export const createComponentInstance = ( component: BlockFn, diff --git a/packages/runtime-vapor/src/directives.ts b/packages/runtime-vapor/src/directives.ts index 03d8c62df0..f5024abfbe 100644 --- a/packages/runtime-vapor/src/directives.ts +++ b/packages/runtime-vapor/src/directives.ts @@ -1,8 +1,8 @@ -import { isFunction } from '@vue/shared' -import { currentInstance, type ComponentPublicInstance } from './component' +import { type Prettify, isFunction } from '@vue/shared' +import { currentInstance, ComponentInternalInstance } from './component' export interface DirectiveBinding { - instance: ComponentPublicInstance | null + instance: ComponentInternalInstance | null value: V oldValue: V | null arg?: string @@ -21,15 +21,16 @@ export type DirectiveHook = ( // `beforeUnmount`-> node unmount -> `unmounted` export interface ObjectDirective { created?: DirectiveHook - // beforeMount?: DirectiveHook - // mounted?: DirectiveHook + beforeMount?: DirectiveHook + mounted?: DirectiveHook // beforeUpdate?: DirectiveHook // updated?: DirectiveHook - // beforeUnmount?: DirectiveHook - // unmounted?: DirectiveHook + beforeUnmount?: DirectiveHook + unmounted?: DirectiveHook // getSSRProps?: SSRDirectiveHook - deep?: boolean + // deep?: boolean } +export type DirectiveHookName = Exclude export type FunctionDirective = DirectiveHook export type Directive = @@ -54,8 +55,6 @@ export function withDirectives( if (!currentInstance.dirs.has(node)) currentInstance.dirs.set(node, []) const bindings = currentInstance.dirs.get(node)! - // TODO public instance - const instance = currentInstance as any for (const directive of directives) { let [dir, value, arg] = directive if (!dir) continue @@ -68,7 +67,7 @@ export function withDirectives( const binding: DirectiveBinding = { dir, - instance, + instance: currentInstance, value, oldValue: void 0, arg, @@ -79,3 +78,21 @@ export function withDirectives( return node } + +export function invokeDirectiveHook( + instance: ComponentInternalInstance | null, + name: DirectiveHookName, + nodes?: IterableIterator, +) { + if (!instance) return + if (!nodes) { + nodes = instance.dirs.keys() + } + for (const node of nodes) { + const directives = instance.dirs.get(node) || [] + for (const binding of directives) { + const hook = binding.dir[name] + hook && hook(node, binding) + } + } +} diff --git a/packages/runtime-vapor/src/render.ts b/packages/runtime-vapor/src/render.ts index 82a3bfde7e..64d8f0a3d7 100644 --- a/packages/runtime-vapor/src/render.ts +++ b/packages/runtime-vapor/src/render.ts @@ -4,12 +4,13 @@ import { normalizeStyle, toDisplayString, } from '@vue/shared' - import { ComponentInternalInstance, createComponentInstance, setCurrentInstance, + unsetCurrentInstance, } from './component' +import { invokeDirectiveHook } from './directives' export type Block = Node | Fragment | Block[] export type ParentBlock = ParentNode | Node[] @@ -37,11 +38,17 @@ export const mountComponent = ( container: ParentNode, ) => { instance.container = container + + setCurrentInstance(instance) const block = instance.scope.run( () => (instance.block = instance.component()), )! + + invokeDirectiveHook(instance, 'beforeMount') insert(block, instance.container) instance.isMounted = true + invokeDirectiveHook(instance, 'mounted') + // TODO: lifecycle hooks (mounted, ...) // const { m } = instance // m && invoke(m) @@ -49,9 +56,14 @@ export const mountComponent = ( export const unmountComponent = (instance: ComponentInternalInstance) => { const { container, block, scope } = instance + + invokeDirectiveHook(instance, 'beforeUnmount') scope.stop() block && remove(block, container) instance.isMounted = false + invokeDirectiveHook(instance, 'unmounted') + unsetCurrentInstance() + // TODO: lifecycle hooks (unmounted, ...) // const { um } = instance // um && invoke(um) diff --git a/playground/src/directive.vue b/playground/src/directive.vue index 74bb387ef6..56315ecad3 100644 --- a/playground/src/directive.vue +++ b/playground/src/directive.vue @@ -1,12 +1,25 @@