mirror of https://github.com/vuejs/core.git
174 lines
4.3 KiB
TypeScript
174 lines
4.3 KiB
TypeScript
import {
|
|
type ComponentInternalInstance,
|
|
componentKey,
|
|
createSetupContext,
|
|
setCurrentInstance,
|
|
validateComponentName,
|
|
} from './component'
|
|
import { insert, querySelector, remove } from './dom/element'
|
|
import { flushPostFlushCbs, queuePostFlushCb } from './scheduler'
|
|
import { invokeLifecycle } from './componentLifecycle'
|
|
import { VaporLifecycleHooks } from './enums'
|
|
import {
|
|
pauseTracking,
|
|
proxyRefs,
|
|
resetTracking,
|
|
shallowReadonly,
|
|
} from '@vue/reactivity'
|
|
import { isArray, isFunction, isObject } from '@vue/shared'
|
|
import { fallThroughAttrs } from './componentAttrs'
|
|
import { VaporErrorCodes, callWithErrorHandling } from './errorHandling'
|
|
import { endMeasure, startMeasure } from './profiling'
|
|
import { devtoolsComponentAdded } from './devtools'
|
|
|
|
export const fragmentKey = Symbol(__DEV__ ? `fragmentKey` : ``)
|
|
|
|
export type Block = Node | Fragment | ComponentInternalInstance | Block[]
|
|
export type Fragment = {
|
|
nodes: Block
|
|
anchor?: Node
|
|
[fragmentKey]: true
|
|
}
|
|
|
|
export function setupComponent(
|
|
instance: ComponentInternalInstance,
|
|
singleRoot: boolean = false,
|
|
): void {
|
|
if (__DEV__) {
|
|
startMeasure(instance, `init`)
|
|
}
|
|
const reset = setCurrentInstance(instance)
|
|
instance.scope.run(() => {
|
|
const { type: component, props } = instance
|
|
|
|
if (__DEV__) {
|
|
if (component.name) {
|
|
validateComponentName(component.name, instance.appContext.config)
|
|
}
|
|
}
|
|
|
|
const setupFn = isFunction(component) ? component : component.setup
|
|
let stateOrNode: Block | undefined
|
|
if (setupFn) {
|
|
const setupContext = (instance.setupContext =
|
|
setupFn && setupFn.length > 1 ? createSetupContext(instance) : null)
|
|
pauseTracking()
|
|
stateOrNode = callWithErrorHandling(
|
|
setupFn,
|
|
instance,
|
|
VaporErrorCodes.SETUP_FUNCTION,
|
|
[__DEV__ ? shallowReadonly(props) : props, setupContext],
|
|
)
|
|
resetTracking()
|
|
}
|
|
|
|
let block: Block | undefined
|
|
|
|
if (
|
|
stateOrNode &&
|
|
(stateOrNode instanceof Node ||
|
|
isArray(stateOrNode) ||
|
|
fragmentKey in stateOrNode ||
|
|
componentKey in stateOrNode)
|
|
) {
|
|
block = stateOrNode
|
|
} else if (isObject(stateOrNode)) {
|
|
instance.setupState = proxyRefs(stateOrNode)
|
|
}
|
|
if (!block && component.render) {
|
|
pauseTracking()
|
|
block = callWithErrorHandling(
|
|
component.render,
|
|
instance,
|
|
VaporErrorCodes.RENDER_FUNCTION,
|
|
[instance.setupState],
|
|
)
|
|
resetTracking()
|
|
}
|
|
|
|
if (block instanceof DocumentFragment) {
|
|
block = Array.from(block.childNodes)
|
|
}
|
|
if (!block) {
|
|
// TODO: warn no template
|
|
block = []
|
|
}
|
|
instance.block = block
|
|
if (singleRoot) fallThroughAttrs(instance)
|
|
return block
|
|
})
|
|
reset()
|
|
if (__DEV__) {
|
|
endMeasure(instance, `init`)
|
|
}
|
|
}
|
|
|
|
export function render(
|
|
instance: ComponentInternalInstance,
|
|
container: string | ParentNode,
|
|
): void {
|
|
mountComponent(instance, (container = normalizeContainer(container)))
|
|
flushPostFlushCbs()
|
|
}
|
|
|
|
export function normalizeContainer(container: string | ParentNode): ParentNode {
|
|
return typeof container === 'string'
|
|
? (querySelector(container) as ParentNode)
|
|
: container
|
|
}
|
|
|
|
function mountComponent(
|
|
instance: ComponentInternalInstance,
|
|
container: ParentNode,
|
|
) {
|
|
instance.container = container
|
|
|
|
if (__DEV__) {
|
|
startMeasure(instance, 'mount')
|
|
}
|
|
|
|
// hook: beforeMount
|
|
invokeLifecycle(instance, VaporLifecycleHooks.BEFORE_MOUNT, 'beforeMount')
|
|
|
|
insert(instance.block!, instance.container)
|
|
|
|
// hook: mounted
|
|
invokeLifecycle(
|
|
instance,
|
|
VaporLifecycleHooks.MOUNTED,
|
|
'mounted',
|
|
instance => (instance.isMounted = true),
|
|
true,
|
|
)
|
|
|
|
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
|
|
devtoolsComponentAdded(instance)
|
|
}
|
|
|
|
if (__DEV__) {
|
|
endMeasure(instance, 'mount')
|
|
}
|
|
|
|
return instance
|
|
}
|
|
|
|
export function unmountComponent(instance: ComponentInternalInstance) {
|
|
const { container, block, scope } = instance
|
|
|
|
// hook: beforeUnmount
|
|
invokeLifecycle(instance, VaporLifecycleHooks.BEFORE_UNMOUNT, 'beforeUnmount')
|
|
|
|
scope.stop()
|
|
block && remove(block, container)
|
|
|
|
// hook: unmounted
|
|
invokeLifecycle(
|
|
instance,
|
|
VaporLifecycleHooks.UNMOUNTED,
|
|
'unmounted',
|
|
instance => queuePostFlushCb(() => (instance.isUnmounted = true)),
|
|
true,
|
|
)
|
|
flushPostFlushCbs()
|
|
}
|