--- /dev/null
+import {
+ template,
+ children,
+ effect,
+ setText,
+ render,
+ getCurrentInstance,
+ ref,
+ unmountComponent,
+} from '../src'
+import type { ComponentInternalInstance } from '../src'
+import { afterEach, beforeEach, describe, expect } from 'vitest'
+import { defineComponent, nextTick } from '@vue/runtime-core'
+
+let host: HTMLElement
+
+const initHost = () => {
+ host = document.createElement('div')
+ host.setAttribute('id', 'host')
+ document.body.appendChild(host)
+}
+beforeEach(() => {
+ initHost()
+})
+afterEach(() => {
+ host.remove()
+})
+describe('component', () => {
+ test('unmountComponent', async () => {
+ const Comp = defineComponent({
+ setup() {
+ const count = ref(0)
+ const t0 = template('<div></div>')
+ const n0 = t0()
+ const {
+ 0: [n1],
+ } = children(n0)
+ effect(() => {
+ setText(n1, void 0, count.value)
+ })
+ return n0
+ },
+ })
+ const instance = render(Comp as any, {}, '#host')
+ await nextTick()
+ expect(host.innerHTML).toBe('<div>0</div>')
+ unmountComponent(instance)
+ expect(host.innerHTML).toBe('')
+ })
+})
}
export function remove(block: Block, parent: ParentNode) {
- if (block instanceof Node) {
+ if (block instanceof DocumentFragment) {
+ remove(Array.from(block.childNodes), parent)
+ } else if (block instanceof Node) {
parent.removeChild(block)
} else if (isArray(block)) {
for (const child of block) remove(child, parent)
}
}
-export function setText(el: Element, oldVal: any, newVal: any) {
+export function setText(el: Node, oldVal: any, newVal: any) {
if ((newVal = toDisplayString(newVal)) !== oldVal) {
el.textContent = newVal
}
}
type Children = Record<number, [ChildNode, Children]>
-export function children(n: ChildNode): Children {
+export function children(n: Node): Children {
return { ...Array.from(n.childNodes).map((n) => [n, children(n)]) }
}
export * from './directive'
export * from './dom'
export * from './directives/vShow'
+export { getCurrentInstance, type ComponentInternalInstance } from './component'
new Proxy({ _: instance }, PublicInstanceProxyHandlers),
)
const state = setupFn && setupFn(props, ctx)
+ let block: Block | null = null
if (state && '__isScriptSetup' in state) {
instance.setupState = proxyRefs(state)
- return (instance.block = component.render(instance.proxy))
+ block = component.render(instance.proxy)
} else {
- return (instance.block = state as Block)
+ block = state as Block
}
+ if (block instanceof DocumentFragment) block = Array.from(block.childNodes)
+ return (instance.block = block)
})!
invokeDirectiveHook(instance, 'beforeMount')
insert(block, instance.container)
-export const template = (str: string): (() => Node) => {
+export const template = (str: string): (() => DocumentFragment) => {
let cached = false
let node: DocumentFragment
return () => {
// repeated renders: clone from cache. This is more performant and
// efficient when dealing with big lists where the template is repeated
// many times.
- return node.cloneNode(true)
+ return node.cloneNode(true) as DocumentFragment
}
}
}
--- /dev/null
+import { template } from '@vue/runtime-vapor'
+
+export default () => {
+ return template('<div>')()
+}