diff --git a/packages/runtime-vapor/__tests__/component.spec.ts b/packages/runtime-vapor/__tests__/component.spec.ts
new file mode 100644
index 000000000..0d9b26f7a
--- /dev/null
+++ b/packages/runtime-vapor/__tests__/component.spec.ts
@@ -0,0 +1,50 @@
+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('
')
+ 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('0
')
+ unmountComponent(instance)
+ expect(host.innerHTML).toBe('')
+ })
+})
diff --git a/packages/runtime-vapor/src/dom.ts b/packages/runtime-vapor/src/dom.ts
index 92bc1e252..d102433e3 100644
--- a/packages/runtime-vapor/src/dom.ts
+++ b/packages/runtime-vapor/src/dom.ts
@@ -42,7 +42,9 @@ export function append(parent: ParentBlock, ...nodes: Node[]) {
}
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)
@@ -52,7 +54,7 @@ export function remove(block: Block, parent: ParentNode) {
}
}
-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
}
@@ -104,7 +106,7 @@ export function setDynamicProp(el: Element, key: string, val: any) {
}
type Children = Record
-export function children(n: ChildNode): Children {
+export function children(n: Node): Children {
return { ...Array.from(n.childNodes).map((n) => [n, children(n)]) }
}
diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts
index 0c56476ad..3a88a738c 100644
--- a/packages/runtime-vapor/src/index.ts
+++ b/packages/runtime-vapor/src/index.ts
@@ -44,3 +44,4 @@ export * from './scheduler'
export * from './directive'
export * from './dom'
export * from './directives/vShow'
+export { getCurrentInstance, type ComponentInternalInstance } from './component'
diff --git a/packages/runtime-vapor/src/render.ts b/packages/runtime-vapor/src/render.ts
index f55acd576..e39ace1d0 100644
--- a/packages/runtime-vapor/src/render.ts
+++ b/packages/runtime-vapor/src/render.ts
@@ -54,12 +54,15 @@ export function mountComponent(
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)
diff --git a/packages/runtime-vapor/src/template.ts b/packages/runtime-vapor/src/template.ts
index aa3858b24..5f12b80e6 100644
--- a/packages/runtime-vapor/src/template.ts
+++ b/packages/runtime-vapor/src/template.ts
@@ -1,4 +1,4 @@
-export const template = (str: string): (() => Node) => {
+export const template = (str: string): (() => DocumentFragment) => {
let cached = false
let node: DocumentFragment
return () => {
@@ -14,7 +14,7 @@ export const template = (str: string): (() => Node) => {
// 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
}
}
}
diff --git a/playground/src/no-script.js b/playground/src/no-script.js
new file mode 100644
index 000000000..6394e5264
--- /dev/null
+++ b/playground/src/no-script.js
@@ -0,0 +1,5 @@
+import { template } from '@vue/runtime-vapor'
+
+export default () => {
+ return template('')()
+}