From: daiwei Date: Thu, 24 Apr 2025 03:52:39 +0000 (+0800) Subject: wip: hydrate v-if X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=38d4889de75da36e1d501ff702193829c81fa8f4;p=thirdparty%2Fvuejs%2Fcore.git wip: hydrate v-if --- diff --git a/packages/runtime-vapor/__tests__/hydration.spec.ts b/packages/runtime-vapor/__tests__/hydration.spec.ts index ecb8d8202a..b02f50177a 100644 --- a/packages/runtime-vapor/__tests__/hydration.spec.ts +++ b/packages/runtime-vapor/__tests__/hydration.spec.ts @@ -1,5 +1,5 @@ import { createVaporSSRApp, delegateEvents } from '../src' -import { nextTick, ref } from '@vue/runtime-dom' +import { nextTick, reactive, ref } from '@vue/runtime-dom' import { compileScript, parse } from '@vue/compiler-sfc' import * as runtimeVapor from '../src' import * as runtimeDom from '@vue/runtime-dom' @@ -50,8 +50,8 @@ function compile( async function testHydration( code: string, components: Record = {}, + data: any = ref('foo'), ) { - const data = ref('foo') const ssrComponents: any = {} const clientComponents: any = {} for (const key in components) { @@ -638,7 +638,100 @@ describe('Vapor Mode hydration', () => { ) }) - test.todo('if') + describe('if', () => { + test('basic toggle - true -> false', async () => { + const data = ref(true) + const { container } = await testHydration( + ``, + undefined, + data, + ) + expect(container.innerHTML).toMatchInlineSnapshot( + `"
foo
"`, + ) + + data.value = false + await nextTick() + expect(container.innerHTML).toMatchInlineSnapshot(`""`) + }) + + test('basic toggle - false -> true', async () => { + const data = ref(false) + const { container } = await testHydration( + ``, + undefined, + data, + ) + expect(container.innerHTML).toMatchInlineSnapshot(`""`) + + data.value = true + await nextTick() + expect(container.innerHTML).toMatchInlineSnapshot( + `"
foo
"`, + ) + }) + + test('v-if/else-if/else chain - switch branches', async () => { + const data = ref('a') + const { container } = await testHydration( + ``, + undefined, + data, + ) + expect(container.innerHTML).toMatchInlineSnapshot( + `"
foo
"`, + ) + + data.value = 'b' + await nextTick() + expect(container.innerHTML).toMatchInlineSnapshot( + `"
bar
"`, + ) + + data.value = 'c' + await nextTick() + expect(container.innerHTML).toMatchInlineSnapshot( + `"
baz
"`, + ) + }) + + test('nested if', async () => { + const data = reactive({ outer: true, inner: true }) + const { container } = await testHydration( + ``, + undefined, + data, + ) + expect(container.innerHTML).toMatchInlineSnapshot( + `"
outer
inner
"`, + ) + + data.inner = false + await nextTick() + expect(container.innerHTML).toMatchInlineSnapshot( + `"
outer
"`, + ) + + data.outer = false + await nextTick() + expect(container.innerHTML).toMatchInlineSnapshot(`""`) + }) + + test.todo('on component', async () => {}) + }) test.todo('for') diff --git a/packages/runtime-vapor/src/apiCreateIf.ts b/packages/runtime-vapor/src/apiCreateIf.ts index 71bfa32d5d..6210e9221b 100644 --- a/packages/runtime-vapor/src/apiCreateIf.ts +++ b/packages/runtime-vapor/src/apiCreateIf.ts @@ -1,5 +1,10 @@ import { type Block, type BlockFn, DynamicFragment, insert } from './block' -import { isHydrating, locateHydrationNode } from './dom/hydration' +import { + currentHydrationNode, + isComment, + isHydrating, + locateHydrationNode, +} from './dom/hydration' import { insertionAnchor, insertionParent } from './insertionState' import { renderEffect } from './renderEffect' @@ -11,8 +16,10 @@ export function createIf( ): Block { const _insertionParent = insertionParent const _insertionAnchor = insertionAnchor + let _currentHydrationNode if (isHydrating) { locateHydrationNode() + _currentHydrationNode = currentHydrationNode } let frag: Block @@ -27,5 +34,23 @@ export function createIf( insert(frag, _insertionParent, _insertionAnchor) } + // if the current hydration node is a comment, use it as an anchor + // otherwise need to insert the anchor node + // OR adjust ssr output to add anchor for v-if + else if (isHydrating && _currentHydrationNode) { + const parentNode = _currentHydrationNode.parentNode + if (parentNode) { + if (isComment(_currentHydrationNode, '')) { + if (__DEV__) _currentHydrationNode.data = 'if' + ;(frag as DynamicFragment).anchor = _currentHydrationNode + } else { + parentNode.insertBefore( + (frag as DynamicFragment).anchor, + _currentHydrationNode.nextSibling, + ) + } + } + } + return frag }