Преглед изворни кода

feat(hydration): handle consecutive if node

daiwei пре 11 месеци
родитељ
комит
f28cb2f678

+ 20 - 0
packages/runtime-vapor/__tests__/hydration.spec.ts

@@ -1269,6 +1269,26 @@ describe('Vapor Mode hydration', () => {
       expect(container.innerHTML).toBe(`<!--${anchorLabel}-->`)
     })
 
+    test('consecutive if node', async () => {
+      const data = ref(true)
+      const { container } = await testHydration(
+        `<template>
+          <components.Child v-if="data"/>
+        </template>`,
+        { Child: `<template><div v-if="data">foo</div></template>` },
+        data,
+      )
+      expect(container.innerHTML).toBe(`<div>foo</div><!--if--><!--if-->`)
+
+      data.value = false
+      await nextTick()
+      expect(container.innerHTML).toBe(`<!--if-->`)
+
+      data.value = true
+      await nextTick()
+      expect(container.innerHTML).toBe(`<div>foo</div><!--if--><!--if-->`)
+    })
+
     test('v-if/else-if/else chain on component - switch branches', async () => {
       const data = ref('a')
       const { container } = await testHydration(

+ 10 - 6
packages/runtime-vapor/src/dom/hydration.ts

@@ -20,6 +20,10 @@ export function setCurrentHydrationNode(node: Node | null): void {
   currentHydrationNode = node
 }
 
+export function advanceHydrationNode(node: Node): void {
+  setCurrentHydrationNode(node.nextSibling || node.parentNode)
+}
+
 let isOptimized = false
 
 function performHydration<T>(
@@ -96,7 +100,7 @@ function adoptTemplateImpl(node: Node, template: string): Node | null {
     }
   }
 
-  currentHydrationNode = __next(node)
+  advanceHydrationNode(node)
   return node
 }
 
@@ -194,12 +198,12 @@ export function isNonHydrationNode(node: Node): boolean {
 export function locateVaporFragmentAnchor(
   node: Node,
   anchorLabel: string,
-): Comment | undefined {
-  let n = node.nextSibling
-  while (n) {
-    if (isComment(n, anchorLabel)) return n
-    n = n.nextSibling
+): Comment | null {
+  while (node) {
+    if (isComment(node, anchorLabel)) return node
+    node = node.nextSibling!
   }
+  return null
 }
 
 export function isEmptyTextNode(node: Node): node is Text {

+ 7 - 6
packages/runtime-vapor/src/fragment.ts

@@ -11,12 +11,12 @@ import {
 } from './block'
 import type { TransitionHooks } from '@vue/runtime-dom'
 import {
+  advanceHydrationNode,
   currentHydrationNode,
   isComment,
   isHydrating,
   locateHydrationNode,
   locateVaporFragmentAnchor,
-  setCurrentHydrationNode,
 } from './dom/hydration'
 import {
   applyTransitionHooks,
@@ -60,12 +60,13 @@ export class DynamicFragment extends VaporFragment {
    */
   forwarded?: boolean
   teardown?: () => void
+  anchorLabel?: string
 
   constructor(anchorLabel?: string) {
     super([])
     if (isHydrating) {
       locateHydrationNode(anchorLabel === 'slot')
-      this.hydrate(anchorLabel!)
+      this.anchorLabel = anchorLabel
     } else {
       this.anchor =
         __DEV__ && anchorLabel ? createComment(anchorLabel) : createTextNode()
@@ -74,12 +75,13 @@ export class DynamicFragment extends VaporFragment {
 
   update(render?: BlockFn, key: any = render): void {
     if (key === this.current) {
+      if (isHydrating) this.hydrate(this.anchorLabel!)
       return
     }
     this.current = key
 
     const prevSub = setActiveSub()
-    const parent = this.anchor.parentNode
+    const parent = isHydrating ? null : this.anchor.parentNode
     const transition = this.$transition
     const renderBranch = () => {
       if (render) {
@@ -137,10 +139,8 @@ export class DynamicFragment extends VaporFragment {
       }
     }
 
-    if (isHydrating) {
-      setCurrentHydrationNode(this.anchor.nextSibling)
-    }
     setActiveSub(prevSub)
+    if (isHydrating) this.hydrate(this.anchorLabel!)
   }
 
   hydrate(label: string): void {
@@ -161,6 +161,7 @@ export class DynamicFragment extends VaporFragment {
         throw new Error(`${label} fragment anchor node was not found.`)
       }
     }
+    advanceHydrationNode(this.anchor)
   }
 }