vue3-core/packages/runtime-vapor/src/apiRender.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

100 lines
2.8 KiB
TypeScript
Raw Normal View History

import { isArray, isFunction, isObject } from '@vue/shared'
import { type ComponentInternalInstance, setCurrentInstance } from './component'
import { insert, querySelector, remove } from './dom/element'
import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler'
import { proxyRefs } from '@vue/reactivity'
import { invokeLifecycle } from './componentLifecycle'
import { VaporLifecycleHooks } from './apiLifecycle'
2023-11-17 03:01:19 +08:00
export const fragmentKey = Symbol(__DEV__ ? `fragmentKey` : ``)
2024-01-28 20:15:41 +08:00
2023-11-17 03:01:19 +08:00
export type Block = Node | Fragment | Block[]
2024-01-28 20:15:41 +08:00
export type Fragment = {
nodes: Block
anchor?: Node
[fragmentKey]: true
}
2023-11-17 03:01:19 +08:00
export function setupComponent(instance: ComponentInternalInstance): void {
const reset = setCurrentInstance(instance)
instance.scope.run(() => {
const { component, props, emit, attrs } = instance
const ctx = { expose: () => {}, emit, attrs }
2023-12-06 14:59:11 +08:00
const setupFn = isFunction(component) ? component : component.setup
const stateOrNode = setupFn && setupFn(props, ctx)
let block: Block | undefined
2024-01-28 20:15:41 +08:00
if (
stateOrNode &&
(stateOrNode instanceof Node ||
isArray(stateOrNode) ||
(stateOrNode as any)[fragmentKey])
) {
block = stateOrNode as Block
} else if (isObject(stateOrNode)) {
2024-01-20 20:22:06 +08:00
instance.setupState = proxyRefs(stateOrNode)
}
if (!block && component.render) {
2024-01-20 20:22:06 +08:00
block = component.render(instance.setupState)
2023-12-06 14:59:11 +08:00
}
2023-12-15 01:30:26 +08:00
if (block instanceof DocumentFragment) {
block = Array.from(block.childNodes)
}
if (!block) {
// TODO: warn no template
block = []
}
return (instance.block = block)
})
reset()
}
export function render(
instance: ComponentInternalInstance,
container: string | ParentNode,
): void {
mountComponent(instance, (container = normalizeContainer(container)))
flushPostFlushCbs()
}
function normalizeContainer(container: string | ParentNode): ParentNode {
return typeof container === 'string'
? (querySelector(container) as ParentNode)
: container
}
function mountComponent(
instance: ComponentInternalInstance,
container: ParentNode,
) {
instance.container = container
// hook: beforeMount
invokeLifecycle(instance, VaporLifecycleHooks.BEFORE_MOUNT, 'beforeMount')
insert(instance.block!, instance.container)
instance.isMounted = true
// hook: mounted
invokeLifecycle(instance, VaporLifecycleHooks.MOUNTED, 'mounted', true)
2023-12-04 16:08:15 +08:00
return instance
}
2023-12-06 14:59:11 +08:00
export function unmountComponent(instance: ComponentInternalInstance) {
const { container, block, scope } = instance
2023-12-04 16:08:15 +08:00
// hook: beforeUnmount
invokeLifecycle(instance, VaporLifecycleHooks.BEFORE_UNMOUNT, 'beforeUnmount')
scope.stop()
block && remove(block, container)
// hook: unmounted
invokeLifecycle(instance, VaporLifecycleHooks.UNMOUNTED, 'unmounted', true)
queuePostRenderEffect(() => (instance.isUnmounted = true))
}