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('
')() +}