Просмотр исходного кода

wip: extract VaporFragment into a separate file

 to resolve the circular dependency caused by TeleportFragment
daiwei 1 год назад
Родитель
Сommit
5b933f582c

+ 31 - 30
packages-private/vapor-e2e-test/__tests__/teleport.spec.ts

@@ -9,7 +9,7 @@ import { nextTick } from 'vue'
 import { ports } from '../utils'
 import { ports } from '../utils'
 const { page, click, html } = setupPuppeteer()
 const { page, click, html } = setupPuppeteer()
 
 
-describe('vdom / vapor interop', () => {
+describe('vapor teleport', () => {
   let server: any
   let server: any
   const port = ports.teleport
   const port = ports.teleport
   beforeAll(() => {
   beforeAll(() => {
@@ -29,33 +29,34 @@ describe('vdom / vapor interop', () => {
     await page().waitForSelector('#app')
     await page().waitForSelector('#app')
   })
   })
 
 
-  describe('vapor teleport', () => {
-    test(
-      'render vdom component',
-      async () => {
-        const targetSelector = '.target'
-        const testSelector = '.interop-render-vdom-comp'
-        const containerSelector = `${testSelector} > div`
-        const btnSelector = `${testSelector} > button`
-
-        // teleport is disabled
-        expect(await html(containerSelector)).toBe('<h1>vdom comp</h1>')
-        expect(await html(targetSelector)).toBe('')
-
-        // enable teleport
-        await click(btnSelector)
-        await nextTick()
-
-        expect(await html(containerSelector)).toBe('')
-        expect(await html(targetSelector)).toBe('<h1>vdom comp</h1>')
-
-        // disable teleport
-        await click(btnSelector)
-        await nextTick()
-        expect(await html(containerSelector)).toBe('<h1>vdom comp</h1>')
-        expect(await html(targetSelector)).toBe('')
-      },
-      E2E_TIMEOUT,
-    )
-  })
+  test(
+    'render vdom component',
+    async () => {
+      const targetSelector = '.target'
+      const testSelector = '.interop-render-vdom-comp'
+      const containerSelector = `${testSelector} > div`
+      const btnSelector = `${testSelector} > button`
+
+      const tt = await html('#app')
+      console.log(tt)
+
+      // teleport is disabled
+      expect(await html(containerSelector)).toBe('<h1>vdom comp</h1>')
+      expect(await html(targetSelector)).toBe('')
+
+      // enable teleport
+      await click(btnSelector)
+      await nextTick()
+
+      expect(await html(containerSelector)).toBe('')
+      expect(await html(targetSelector)).toBe('<h1>vdom comp</h1>')
+
+      // disable teleport
+      await click(btnSelector)
+      await nextTick()
+      expect(await html(containerSelector)).toBe('<h1>vdom comp</h1>')
+      expect(await html(targetSelector)).toBe('')
+    },
+    E2E_TIMEOUT,
+  )
 })
 })

+ 2 - 7
packages/runtime-vapor/__tests__/block.spec.ts

@@ -1,10 +1,5 @@
-import {
-  VaporFragment,
-  insert,
-  normalizeBlock,
-  prepend,
-  remove,
-} from '../src/block'
+import { insert, normalizeBlock, prepend, remove } from '../src/block'
+import { VaporFragment } from '../src/fragment'
 
 
 const node1 = document.createTextNode('node1')
 const node1 = document.createTextNode('node1')
 const node2 = document.createTextNode('node2')
 const node2 = document.createTextNode('node2')

+ 1 - 1
packages/runtime-vapor/src/apiCreateDynamicComponent.ts

@@ -1,9 +1,9 @@
 import { resolveDynamicComponent } from '@vue/runtime-dom'
 import { resolveDynamicComponent } from '@vue/runtime-dom'
-import { DynamicFragment, type VaporFragment } from './block'
 import { createComponentWithFallback } from './component'
 import { createComponentWithFallback } from './component'
 import { renderEffect } from './renderEffect'
 import { renderEffect } from './renderEffect'
 import type { RawProps } from './componentProps'
 import type { RawProps } from './componentProps'
 import type { RawSlots } from './componentSlots'
 import type { RawSlots } from './componentSlots'
+import { DynamicFragment, type VaporFragment } from './fragment'
 
 
 export function createDynamicComponent(
 export function createDynamicComponent(
   getter: () => any,
   getter: () => any,

+ 2 - 6
packages/runtime-vapor/src/apiCreateFor.ts

@@ -11,12 +11,7 @@ import {
 } from '@vue/reactivity'
 } from '@vue/reactivity'
 import { getSequence, isArray, isObject, isString } from '@vue/shared'
 import { getSequence, isArray, isObject, isString } from '@vue/shared'
 import { createComment, createTextNode } from './dom/node'
 import { createComment, createTextNode } from './dom/node'
-import {
-  type Block,
-  VaporFragment,
-  insert,
-  remove as removeBlock,
-} from './block'
+import { type Block, insert, remove as removeBlock } from './block'
 import { warn } from '@vue/runtime-dom'
 import { warn } from '@vue/runtime-dom'
 import { currentInstance, isVaporComponent } from './component'
 import { currentInstance, isVaporComponent } from './component'
 import type { DynamicSlot } from './componentSlots'
 import type { DynamicSlot } from './componentSlots'
@@ -24,6 +19,7 @@ import { renderEffect } from './renderEffect'
 import { VaporVForFlags } from '../../shared/src/vaporFlags'
 import { VaporVForFlags } from '../../shared/src/vaporFlags'
 import { isHydrating, locateHydrationNode } from './dom/hydration'
 import { isHydrating, locateHydrationNode } from './dom/hydration'
 import { insertionAnchor, insertionParent } from './insertionState'
 import { insertionAnchor, insertionParent } from './insertionState'
+import { VaporFragment } from './fragment'
 
 
 class ForBlock extends VaporFragment {
 class ForBlock extends VaporFragment {
   scope: EffectScope | undefined
   scope: EffectScope | undefined

+ 2 - 1
packages/runtime-vapor/src/apiCreateIf.ts

@@ -1,7 +1,8 @@
-import { type Block, type BlockFn, DynamicFragment, insert } from './block'
+import { type Block, type BlockFn, insert } from './block'
 import { isHydrating, locateHydrationNode } from './dom/hydration'
 import { isHydrating, locateHydrationNode } from './dom/hydration'
 import { insertionAnchor, insertionParent } from './insertionState'
 import { insertionAnchor, insertionParent } from './insertionState'
 import { renderEffect } from './renderEffect'
 import { renderEffect } from './renderEffect'
+import { DynamicFragment } from './fragment'
 
 
 export function createIf(
 export function createIf(
   condition: () => any,
   condition: () => any,

+ 5 - 68
packages/runtime-vapor/src/block.ts

@@ -5,9 +5,12 @@ import {
   mountComponent,
   mountComponent,
   unmountComponent,
   unmountComponent,
 } from './component'
 } from './component'
-import { createComment, createTextNode } from './dom/node'
-import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
 import { isHydrating } from './dom/hydration'
 import { isHydrating } from './dom/hydration'
+import {
+  type DynamicFragment,
+  type VaporFragment,
+  isFragment,
+} from './fragment'
 
 
 export type Block =
 export type Block =
   | Node
   | Node
@@ -18,72 +21,6 @@ export type Block =
 
 
 export type BlockFn = (...args: any[]) => Block
 export type BlockFn = (...args: any[]) => Block
 
 
-export class VaporFragment {
-  nodes: Block
-  target?: ParentNode | null
-  targetAnchor?: Node | null
-  anchor?: Node
-  insert?: (parent: ParentNode, anchor: Node | null) => void
-  remove?: (parent?: ParentNode) => void
-  getNodes?: () => Block
-
-  constructor(nodes: Block) {
-    this.nodes = nodes
-  }
-}
-
-export class DynamicFragment extends VaporFragment {
-  anchor: Node
-  scope: EffectScope | undefined
-  current?: BlockFn
-  fallback?: BlockFn
-
-  constructor(anchorLabel?: string) {
-    super([])
-    this.anchor =
-      __DEV__ && anchorLabel ? createComment(anchorLabel) : createTextNode()
-  }
-
-  update(render?: BlockFn, key: any = render): void {
-    if (key === this.current) {
-      return
-    }
-    this.current = key
-
-    pauseTracking()
-    const parent = this.anchor.parentNode
-
-    // teardown previous branch
-    if (this.scope) {
-      this.scope.stop()
-      parent && remove(this.nodes, parent)
-    }
-
-    if (render) {
-      this.scope = new EffectScope()
-      this.nodes = this.scope.run(render) || []
-      if (parent) insert(this.nodes, parent, this.anchor)
-    } else {
-      this.scope = undefined
-      this.nodes = []
-    }
-
-    if (this.fallback && !isValidBlock(this.nodes)) {
-      parent && remove(this.nodes, parent)
-      this.nodes =
-        (this.scope || (this.scope = new EffectScope())).run(this.fallback) ||
-        []
-      parent && insert(this.nodes, parent, this.anchor)
-    }
-
-    resetTracking()
-  }
-}
-
-export function isFragment(val: NonNullable<unknown>): val is VaporFragment {
-  return val instanceof VaporFragment
-}
-
 export function isBlock(val: NonNullable<unknown>): val is Block {
 export function isBlock(val: NonNullable<unknown>): val is Block {
   return (
   return (
     val instanceof Node ||
     val instanceof Node ||

+ 2 - 1
packages/runtime-vapor/src/componentSlots.ts

@@ -1,11 +1,12 @@
 import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
 import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
-import { type Block, type BlockFn, DynamicFragment, insert } from './block'
+import { type Block, type BlockFn, insert } from './block'
 import { rawPropsProxyHandlers } from './componentProps'
 import { rawPropsProxyHandlers } from './componentProps'
 import { currentInstance, isRef } from '@vue/runtime-dom'
 import { currentInstance, isRef } from '@vue/runtime-dom'
 import type { LooseRawProps, VaporComponentInstance } from './component'
 import type { LooseRawProps, VaporComponentInstance } from './component'
 import { renderEffect } from './renderEffect'
 import { renderEffect } from './renderEffect'
 import { insertionAnchor, insertionParent } from './insertionState'
 import { insertionAnchor, insertionParent } from './insertionState'
 import { isHydrating, locateHydrationNode } from './dom/hydration'
 import { isHydrating, locateHydrationNode } from './dom/hydration'
+import { DynamicFragment } from './fragment'
 
 
 export type RawSlots = Record<string, VaporSlot> & {
 export type RawSlots = Record<string, VaporSlot> & {
   $?: DynamicSlotSource[]
   $?: DynamicSlotSource[]

+ 4 - 9
packages/runtime-vapor/src/components/Teleport.ts

@@ -7,13 +7,7 @@ import {
   resolveTarget,
   resolveTarget,
   warn,
   warn,
 } from '@vue/runtime-dom'
 } from '@vue/runtime-dom'
-import {
-  type Block,
-  type BlockFn,
-  VaporFragment,
-  insert,
-  remove,
-} from '../block'
+import { type Block, type BlockFn, insert, remove } from '../block'
 import { createComment, createTextNode, querySelector } from '../dom/node'
 import { createComment, createTextNode, querySelector } from '../dom/node'
 import type {
 import type {
   LooseRawProps,
   LooseRawProps,
@@ -24,6 +18,7 @@ import { rawPropsProxyHandlers } from '../componentProps'
 import { renderEffect } from '../renderEffect'
 import { renderEffect } from '../renderEffect'
 import { extend, isArray } from '@vue/shared'
 import { extend, isArray } from '@vue/shared'
 import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
 import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
+import { VaporFragment } from '../fragment'
 
 
 export const teleportStack: TeleportFragment[] = __DEV__
 export const teleportStack: TeleportFragment[] = __DEV__
   ? ([] as TeleportFragment[])
   ? ([] as TeleportFragment[])
@@ -70,11 +65,11 @@ export const VaporTeleportImpl = {
     const scope = (frag.scope = new EffectScope())
     const scope = (frag.scope = new EffectScope())
     scope!.run(() => {
     scope!.run(() => {
       renderEffect(() => {
       renderEffect(() => {
-        teleportStack.push(frag)
+        __DEV__ && teleportStack.push(frag)
         frag.updateChildren(
         frag.updateChildren(
           (frag.children = slots.default && (slots.default as BlockFn)()),
           (frag.children = slots.default && (slots.default as BlockFn)()),
         )
         )
-        teleportStack.pop()
+        __DEV__ && teleportStack.pop()
       })
       })
 
 
       renderEffect(() => {
       renderEffect(() => {

+ 2 - 1
packages/runtime-vapor/src/directives/vShow.ts

@@ -6,8 +6,9 @@ import {
 } from '@vue/runtime-dom'
 } from '@vue/runtime-dom'
 import { renderEffect } from '../renderEffect'
 import { renderEffect } from '../renderEffect'
 import { isVaporComponent } from '../component'
 import { isVaporComponent } from '../component'
-import { type Block, DynamicFragment } from '../block'
+import type { Block } from '../block'
 import { isArray } from '@vue/shared'
 import { isArray } from '@vue/shared'
+import { DynamicFragment } from '../fragment'
 
 
 export function applyVShow(target: Block, source: () => any): void {
 export function applyVShow(target: Block, source: () => any): void {
   if (isVaporComponent(target)) {
   if (isVaporComponent(target)) {

+ 69 - 0
packages/runtime-vapor/src/fragment.ts

@@ -0,0 +1,69 @@
+import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
+import { createComment, createTextNode } from './dom/node'
+import { type Block, type BlockFn, insert, isValidBlock, remove } from './block'
+
+export class VaporFragment {
+  nodes: Block
+  target?: ParentNode | null
+  targetAnchor?: Node | null
+  anchor?: Node
+  insert?: (parent: ParentNode, anchor: Node | null) => void
+  remove?: (parent?: ParentNode) => void
+  getNodes?: () => Block
+
+  constructor(nodes: Block) {
+    this.nodes = nodes
+  }
+}
+
+export class DynamicFragment extends VaporFragment {
+  anchor: Node
+  scope: EffectScope | undefined
+  current?: BlockFn
+  fallback?: BlockFn
+
+  constructor(anchorLabel?: string) {
+    super([])
+    this.anchor =
+      __DEV__ && anchorLabel ? createComment(anchorLabel) : createTextNode()
+  }
+
+  update(render?: BlockFn, key: any = render): void {
+    if (key === this.current) {
+      return
+    }
+    this.current = key
+
+    pauseTracking()
+    const parent = this.anchor.parentNode
+
+    // teardown previous branch
+    if (this.scope) {
+      this.scope.stop()
+      parent && remove(this.nodes, parent)
+    }
+
+    if (render) {
+      this.scope = new EffectScope()
+      this.nodes = this.scope.run(render) || []
+      if (parent) insert(this.nodes, parent, this.anchor)
+    } else {
+      this.scope = undefined
+      this.nodes = []
+    }
+
+    if (this.fallback && !isValidBlock(this.nodes)) {
+      parent && remove(this.nodes, parent)
+      this.nodes =
+        (this.scope || (this.scope = new EffectScope())).run(this.fallback) ||
+        []
+      parent && insert(this.nodes, parent, this.anchor)
+    }
+
+    resetTracking()
+  }
+}
+
+export function isFragment(val: NonNullable<unknown>): val is VaporFragment {
+  return val instanceof VaporFragment
+}

+ 3 - 1
packages/runtime-vapor/src/index.ts

@@ -6,7 +6,7 @@ export type { VaporDirective } from './directives/custom'
 export { VaporTeleport } from './components/Teleport'
 export { VaporTeleport } from './components/Teleport'
 
 
 // compiler-use only
 // compiler-use only
-export { insert, prepend, remove, isFragment, VaporFragment } from './block'
+export { insert, prepend, remove } from './block'
 export { setInsertionState } from './insertionState'
 export { setInsertionState } from './insertionState'
 export { createComponent, createComponentWithFallback } from './component'
 export { createComponent, createComponentWithFallback } from './component'
 export { renderEffect } from './renderEffect'
 export { renderEffect } from './renderEffect'
@@ -43,3 +43,5 @@ export {
   applyDynamicModel,
   applyDynamicModel,
 } from './directives/vModel'
 } from './directives/vModel'
 export { withVaporDirectives } from './directives/custom'
 export { withVaporDirectives } from './directives/custom'
+export { isFragment } from './fragment'
+export { VaporFragment } from './fragment'

+ 2 - 1
packages/runtime-vapor/src/vdomInterop.ts

@@ -26,13 +26,14 @@ import {
   mountComponent,
   mountComponent,
   unmountComponent,
   unmountComponent,
 } from './component'
 } from './component'
-import { type Block, VaporFragment, insert, remove } from './block'
+import { type Block, insert, remove } from './block'
 import { EMPTY_OBJ, extend, isFunction } from '@vue/shared'
 import { EMPTY_OBJ, extend, isFunction } from '@vue/shared'
 import { type RawProps, rawPropsProxyHandlers } from './componentProps'
 import { type RawProps, rawPropsProxyHandlers } from './componentProps'
 import type { RawSlots, VaporSlot } from './componentSlots'
 import type { RawSlots, VaporSlot } from './componentSlots'
 import { renderEffect } from './renderEffect'
 import { renderEffect } from './renderEffect'
 import { createTextNode } from './dom/node'
 import { createTextNode } from './dom/node'
 import { optimizePropertyLookup } from './dom/prop'
 import { optimizePropertyLookup } from './dom/prop'
+import { VaporFragment } from './fragment'
 
 
 // mounting vapor components and slots in vdom
 // mounting vapor components and slots in vdom
 const vaporInteropImpl: Omit<
 const vaporInteropImpl: Omit<