]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-vapor): `unmountComponent` (#63)
author白雾三语 <32354856+baiwusanyu-c@users.noreply.github.com>
Thu, 14 Dec 2023 17:23:17 +0000 (01:23 +0800)
committerGitHub <noreply@github.com>
Thu, 14 Dec 2023 17:23:17 +0000 (01:23 +0800)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
packages/runtime-vapor/__tests__/component.spec.ts [new file with mode: 0644]
packages/runtime-vapor/src/dom.ts
packages/runtime-vapor/src/index.ts
packages/runtime-vapor/src/render.ts
packages/runtime-vapor/src/template.ts
playground/src/no-script.js [new file with mode: 0644]

diff --git a/packages/runtime-vapor/__tests__/component.spec.ts b/packages/runtime-vapor/__tests__/component.spec.ts
new file mode 100644 (file)
index 0000000..0d9b26f
--- /dev/null
@@ -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('<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('')
+  })
+})
index 92bc1e252657dc49fb98275e8eaab2fcfa7a3e7d..d102433e3a1db542dc782ab2539b56a875a93df1 100644 (file)
@@ -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<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)]) }
 }
 
index 0c56476ad8b9bcc06b3caf9711358f8d1e4c2316..3a88a738c914e772235d1942b3167aee8ed0a8b5 100644 (file)
@@ -44,3 +44,4 @@ export * from './scheduler'
 export * from './directive'
 export * from './dom'
 export * from './directives/vShow'
+export { getCurrentInstance, type ComponentInternalInstance } from './component'
index f55acd5760b5398557b8c8311ad6d688c11591bd..e39ace1d0778a039970b819c25bffe9603308708 100644 (file)
@@ -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)
index aa3858b24d7acfa6a8548dae03d266f985c71a6d..5f12b80e6521e642842b81aa1d6e5283b6e3fa7b 100644 (file)
@@ -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 (file)
index 0000000..6394e52
--- /dev/null
@@ -0,0 +1,5 @@
+import { template } from '@vue/runtime-vapor'
+
+export default () => {
+  return template('<div>')()
+}