feat: support more directive hook
This commit is contained in:
parent
db151e1b43
commit
f3e80d7706
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<V = any> {
|
||||
instance: ComponentPublicInstance | null
|
||||
instance: ComponentInternalInstance | null
|
||||
value: V
|
||||
oldValue: V | null
|
||||
arg?: string
|
||||
|
@ -21,15 +21,16 @@ export type DirectiveHook<T = any | null, V = any> = (
|
|||
// `beforeUnmount`-> node unmount -> `unmounted`
|
||||
export interface ObjectDirective<T = any, V = any> {
|
||||
created?: DirectiveHook<T, V>
|
||||
// beforeMount?: DirectiveHook<T, V>
|
||||
// mounted?: DirectiveHook<T, V>
|
||||
beforeMount?: DirectiveHook<T, V>
|
||||
mounted?: DirectiveHook<T, V>
|
||||
// beforeUpdate?: DirectiveHook<T, V>
|
||||
// updated?: DirectiveHook<T, V>
|
||||
// beforeUnmount?: DirectiveHook<T, V>
|
||||
// unmounted?: DirectiveHook<T, V>
|
||||
beforeUnmount?: DirectiveHook<T, V>
|
||||
unmounted?: DirectiveHook<T, V>
|
||||
// getSSRProps?: SSRDirectiveHook
|
||||
deep?: boolean
|
||||
// deep?: boolean
|
||||
}
|
||||
export type DirectiveHookName = Exclude<keyof ObjectDirective, 'deep'>
|
||||
|
||||
export type FunctionDirective<T = any, V = any> = DirectiveHook<T, V>
|
||||
export type Directive<T = any, V = any> =
|
||||
|
@ -54,8 +55,6 @@ export function withDirectives<T extends Node>(
|
|||
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<T extends Node>(
|
|||
|
||||
const binding: DirectiveBinding = {
|
||||
dir,
|
||||
instance,
|
||||
instance: currentInstance,
|
||||
value,
|
||||
oldValue: void 0,
|
||||
arg,
|
||||
|
@ -79,3 +78,21 @@ export function withDirectives<T extends Node>(
|
|||
|
||||
return node
|
||||
}
|
||||
|
||||
export function invokeDirectiveHook(
|
||||
instance: ComponentInternalInstance | null,
|
||||
name: DirectiveHookName,
|
||||
nodes?: IterableIterator<Node>,
|
||||
) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,12 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import { FunctionDirective } from '@vue/vapor'
|
||||
import { ObjectDirective } from '@vue/vapor'
|
||||
|
||||
const vDirective: FunctionDirective<HTMLDivElement, undefined> = node => {
|
||||
node.textContent = 'hello world'
|
||||
node.style.color = 'red'
|
||||
const text = 'created (overwrite by v-text), '
|
||||
const vDirective: ObjectDirective<HTMLDivElement, undefined> = {
|
||||
created(node) {
|
||||
if (!node.parentElement) {
|
||||
node.textContent += 'created, '
|
||||
node.style.color = 'red'
|
||||
} else {
|
||||
alert('!')
|
||||
}
|
||||
},
|
||||
beforeMount(node) {
|
||||
if (!node.parentElement) node.textContent += 'beforeMount, '
|
||||
},
|
||||
mounted(node) {
|
||||
if (node.parentElement) node.textContent += 'mounted, '
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-directive />
|
||||
<div v-directive v-text="text" />
|
||||
</template>
|
||||
|
|
Loading…
Reference in New Issue