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

165 lines
4.2 KiB
TypeScript
Raw Normal View History

2023-11-17 03:01:19 +08:00
import {
isArray,
2023-11-17 03:01:19 +08:00
normalizeClass,
normalizeStyle,
2023-12-01 01:28:16 +08:00
toDisplayString,
2023-11-27 23:47:21 +08:00
} from '@vue/shared'
2023-12-03 18:36:01 +08:00
import {
ComponentInternalInstance,
createComponentInstance,
setCurrentInstance,
} from './component'
2023-11-17 03:01:19 +08:00
export type Block = Node | Fragment | Block[]
export type ParentBlock = ParentNode | Node[]
2023-11-26 02:13:59 +08:00
export type Fragment = { nodes: Block; anchor: Node }
2023-11-17 03:01:19 +08:00
export type BlockFn = (props?: any) => Block
export function render(
comp: BlockFn,
2023-12-01 01:28:16 +08:00
container: string | ParentNode,
): ComponentInternalInstance {
const instance = createComponentInstance(comp)
2023-12-03 18:36:01 +08:00
setCurrentInstance(instance)
mountComponent(instance, (container = normalizeContainer(container)))
return instance
2023-11-17 03:01:19 +08:00
}
export function normalizeContainer(container: string | ParentNode): ParentNode {
return typeof container === 'string'
? (document.querySelector(container) as ParentNode)
: container
}
export const mountComponent = (
instance: ComponentInternalInstance,
2023-12-01 01:28:16 +08:00
container: ParentNode,
) => {
instance.container = container
const block = instance.scope.run(
2023-12-01 01:28:16 +08:00
() => (instance.block = instance.component()),
)!
insert(block, instance.container)
instance.isMounted = true
// TODO: lifecycle hooks (mounted, ...)
// const { m } = instance
// m && invoke(m)
}
export const unmountComponent = (instance: ComponentInternalInstance) => {
const { container, block, scope } = instance
scope.stop()
block && remove(block, container)
instance.isMounted = false
// TODO: lifecycle hooks (unmounted, ...)
// const { um } = instance
// um && invoke(um)
}
2023-11-17 03:01:19 +08:00
export function insert(
block: Block,
parent: ParentNode,
2023-12-01 01:28:16 +08:00
anchor: Node | null = null,
2023-11-17 03:01:19 +08:00
) {
// if (!isHydrating) {
if (block instanceof Node) {
parent.insertBefore(block, anchor)
} else if (isArray(block)) {
for (const child of block) insert(child, parent, anchor)
} else {
insert(block.nodes, parent, anchor)
2023-11-26 02:13:59 +08:00
parent.insertBefore(block.anchor, anchor)
2023-11-17 03:01:19 +08:00
}
// }
}
export function prepend(parent: ParentBlock, ...nodes: Node[]) {
if (parent instanceof Node) {
// TODO use insertBefore for better performance https://jsbench.me/rolpg250hh/1
parent.prepend(...nodes)
} else if (isArray(parent)) {
parent.unshift(...nodes)
}
}
export function append(parent: ParentBlock, ...nodes: Node[]) {
if (parent instanceof Node) {
// TODO use insertBefore for better performance
parent.append(...nodes)
} else if (isArray(parent)) {
parent.push(...nodes)
}
2023-11-27 05:16:21 +08:00
}
2023-11-17 03:01:19 +08:00
export function remove(block: Block, parent: ParentNode) {
if (block instanceof Node) {
parent.removeChild(block)
} else if (isArray(block)) {
for (const child of block) remove(child, parent)
} else {
remove(block.nodes, parent)
block.anchor && parent.removeChild(block.anchor)
}
}
export function setText(el: Element, oldVal: any, newVal: any) {
if ((newVal = toDisplayString(newVal)) !== oldVal) {
el.textContent = newVal
}
}
2023-11-24 14:44:57 +08:00
export function setHtml(el: Element, oldVal: any, newVal: any) {
if (newVal !== oldVal) {
el.innerHTML = newVal
}
}
2023-11-17 03:01:19 +08:00
export function setClass(el: Element, oldVal: any, newVal: any) {
if ((newVal = normalizeClass(newVal)) !== oldVal && (newVal || oldVal)) {
el.className = newVal
}
}
export function setStyle(el: HTMLElement, oldVal: any, newVal: any) {
if ((newVal = normalizeStyle(newVal)) !== oldVal && (newVal || oldVal)) {
if (typeof newVal === 'string') {
el.style.cssText = newVal
} else {
// TODO
}
}
}
export function setAttr(el: Element, key: string, oldVal: any, newVal: any) {
if (newVal !== oldVal) {
if (newVal != null) {
el.setAttribute(key, newVal)
} else {
el.removeAttribute(key)
}
}
}
export function setDynamicProp(el: Element, key: string, val: any) {
if (key === 'class') {
setClass(el, void 0, val)
} else if (key === 'style') {
setStyle(el as HTMLElement, void 0, val)
} else if (key in el) {
;(el as any)[key] = val
} else {
// TODO special checks
setAttr(el, key, void 0, val)
}
}
2023-11-23 23:43:19 +08:00
type Children = Record<number, [ChildNode, Children]>
export function children(n: ChildNode): Children {
2023-12-01 01:28:16 +08:00
return { ...Array.from(n.childNodes).map((n) => [n, children(n)]) }
2023-11-23 23:43:19 +08:00
}
2023-11-27 05:16:21 +08:00
export function createTextNode(val: unknown): Text {
return document.createTextNode(toDisplayString(val))
}