From: Johnson Chu Date: Fri, 18 Jul 2025 07:00:04 +0000 (+0800) Subject: fix(compiler-vapor): selectors was not initialized in time when the initial value... X-Git-Tag: v3.6.0-alpha.2~8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f04c9c342d398c11111c873143dc437f588578ee;p=thirdparty%2Fvuejs%2Fcore.git fix(compiler-vapor): selectors was not initialized in time when the initial value of createFor source was not empty (#13642) --- diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap index 2d64e1ffe5..7184446fc0 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap @@ -43,7 +43,7 @@ export function render(_ctx) { const n2 = t0() _setTemplateRef(n2, "foo", void 0, true) return n2 - }, null, 4) + }, undefined, 4) return n0 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap index 141d3e410d..69c695a246 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap @@ -93,7 +93,7 @@ export function render(_ctx) { const x4 = _child(n4) _renderEffect(() => _setText(x4, _toDisplayString(_for_item1.value+_for_item0.value))) return n4 - }, null, 1) + }, undefined, 1) return n5 }) return n0 @@ -150,6 +150,7 @@ exports[`compiler: v-for > selector pattern 1`] = ` const t0 = _template(" ", true) export function render(_ctx) { + let _selector0_0 const n0 = _createFor(() => (_ctx.rows), (_for_item0) => { const n2 = t0() const x2 = _child(n2) @@ -157,8 +158,9 @@ export function render(_ctx) { _setText(x2, _toDisplayString(_ctx.selected === _for_item0.value.id ? 'danger' : '')) }) return n2 - }, (row) => (row.id)) - const _selector0_0 = n0.useSelector(() => _ctx.selected) + }, (row) => (row.id), undefined, ({ createSelector }) => { + _selector0_0 = createSelector(() => _ctx.selected) + }) return n0 }" `; @@ -168,14 +170,16 @@ exports[`compiler: v-for > selector pattern 2`] = ` const t0 = _template("", true) export function render(_ctx) { + let _selector0_0 const n0 = _createFor(() => (_ctx.rows), (_for_item0) => { const n2 = t0() _selector0_0(() => { _setClass(n2, _ctx.selected === _for_item0.value.id ? 'danger' : '') }) return n2 - }, (row) => (row.id)) - const _selector0_0 = n0.useSelector(() => _ctx.selected) + }, (row) => (row.id), undefined, ({ createSelector }) => { + _selector0_0 = createSelector(() => _ctx.selected) + }) return n0 }" `; @@ -202,14 +206,16 @@ exports[`compiler: v-for > selector pattern 4`] = ` const t0 = _template("", true) export function render(_ctx) { + let _selector0_0 const n0 = _createFor(() => (_ctx.rows), (_for_item0) => { const n2 = t0() _selector0_0(() => { _setClass(n2, { danger: _for_item0.value.id === _ctx.selected }) }) return n2 - }, (row) => (row.id)) - const _selector0_0 = n0.useSelector(() => _ctx.selected) + }, (row) => (row.id), undefined, ({ createSelector }) => { + _selector0_0 = createSelector(() => _ctx.selected) + }) return n0 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap index ab3ade45b6..b6107d5a1a 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap @@ -68,7 +68,7 @@ export function render(_ctx) { const n0 = _createFor(() => (_ctx.list), (_for_item0) => { const n2 = t0() return n2 - }, null, 4) + }, undefined, 4) return n0 }" `; diff --git a/packages/compiler-vapor/src/generators/for.ts b/packages/compiler-vapor/src/generators/for.ts index 40f002a853..026db797ba 100644 --- a/packages/compiler-vapor/src/generators/for.ts +++ b/packages/compiler-vapor/src/generators/for.ts @@ -99,19 +99,27 @@ export function genFor( keyProp, idMap, ) - const patternFrag: CodeFragment[] = [] + const selectorDeclarations: CodeFragment[] = [] + const selectorSetup: CodeFragment[] = [] for (let i = 0; i < selectorPatterns.length; i++) { const { selector } = selectorPatterns[i] const selectorName = `_selector${id}_${i}` - patternFrag.push( + selectorDeclarations.push(`let ${selectorName}`, NEWLINE) + if (i === 0) { + selectorSetup.push(`({ createSelector }) => {`, INDENT_START) + } + selectorSetup.push( NEWLINE, - `const ${selectorName} = `, - ...genCall(`n${id}.useSelector`, [ + `${selectorName} = `, + ...genCall(`createSelector`, [ `() => `, ...genExpression(selector, context), ]), ) + if (i === selectorPatterns.length - 1) { + selectorSetup.push(INDENT_END, NEWLINE, '}') + } } const blockFn = context.withId(() => { @@ -165,16 +173,17 @@ export function genFor( return [ NEWLINE, + ...selectorDeclarations, `const n${id} = `, ...genCall( - helper('createFor'), + [helper('createFor'), 'undefined'], sourceExpr, blockFn, genCallback(keyProp), flags ? String(flags) : undefined, + selectorSetup.length ? selectorSetup : undefined, // todo: hydrationNode ), - ...patternFrag, ] // construct a id -> accessor path map. diff --git a/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts b/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts index 9b36a2c311..9a3745fb54 100644 --- a/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts +++ b/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts @@ -1,12 +1,11 @@ import { ref } from '@vue/reactivity' import { makeRender } from './_utils' -// @ts-expect-error -import { createFor, createSelector, renderEffect } from '../src' +import { createFor } from '../src' import { nextTick } from '@vue/runtime-dom' const define = makeRender() -describe.todo('api: createSelector', () => { +describe('api: createSelector', () => { test('basic', async () => { let calledTimes = 0 let expectedCalledTimes = 0 @@ -15,19 +14,23 @@ describe.todo('api: createSelector', () => { const index = ref(0) const { host } = define(() => { - const isSleected = createSelector(index) + let selector: (cb: () => void) => void return createFor( () => list.value, item => { const span = document.createElement('li') - renderEffect(() => { + selector(() => { calledTimes += 1 const { id } = item.value - span.textContent = `${id}.${isSleected(id) ? 't' : 'f'}` + span.textContent = `${id}.${id === index.value ? 't' : 'f'}` }) return span }, item => item.id, + undefined, + ({ createSelector }) => { + selector = createSelector(() => index.value) + }, ) }).render() @@ -50,66 +53,11 @@ describe.todo('api: createSelector', () => { ) expect(calledTimes).toBe((expectedCalledTimes += 2)) - list.value[2].id = 3 - await nextTick() - expect(host.innerHTML).toBe( - '
  • 0.f
  • 1.f
  • 3.f
  • ', - ) - expect(calledTimes).toBe((expectedCalledTimes += 1)) - }) - - test('custom compare', async () => { - let calledTimes = 0 - let expectedCalledTimes = 0 - - const list = ref([{ id: 1 }, { id: 2 }, { id: 3 }]) - const index = ref(0) - - const { host } = define(() => { - const isSleected = createSelector( - index, - // @ts-expect-error - (key, value) => key === value + 1, - ) - return createFor( - () => list.value, - item => { - const span = document.createElement('li') - renderEffect(() => { - calledTimes += 1 - const { id } = item.value - span.textContent = `${id}.${isSleected(id) ? 't' : 'f'}` - }) - return span - }, - item => item.id, - ) - }).render() - - expect(host.innerHTML).toBe( - '
  • 1.t
  • 2.f
  • 3.f
  • ', - ) - expect(calledTimes).toBe((expectedCalledTimes += 3)) - - index.value = 1 - await nextTick() - expect(host.innerHTML).toBe( - '
  • 1.f
  • 2.t
  • 3.f
  • ', - ) - expect(calledTimes).toBe((expectedCalledTimes += 2)) - - index.value = 2 - await nextTick() - expect(host.innerHTML).toBe( - '
  • 1.f
  • 2.f
  • 3.t
  • ', - ) - expect(calledTimes).toBe((expectedCalledTimes += 2)) - - list.value[2].id = 4 - await nextTick() - expect(host.innerHTML).toBe( - '
  • 1.f
  • 2.f
  • 4.f
  • ', - ) - expect(calledTimes).toBe((expectedCalledTimes += 1)) + // list.value[2].id = 3 + // await nextTick() + // expect(host.innerHTML).toBe( + // '
  • 0.f
  • 1.f
  • 3.f
  • ', + // ) + // expect(calledTimes).toBe((expectedCalledTimes += 1)) }) }) diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts index 426a5c56b5..9ffdf6dca5 100644 --- a/packages/runtime-vapor/src/apiCreateFor.ts +++ b/packages/runtime-vapor/src/apiCreateFor.ts @@ -74,6 +74,9 @@ export const createFor = ( ) => Block, getKey?: (item: any, key: any, index?: number) => any, flags = 0, + setup?: (_: { + createSelector: (source: () => any) => (cb: () => void) => void + }) => void, ): VaporFragment => { const _insertionParent = insertionParent const _insertionAnchor = insertionAnchor @@ -402,6 +405,10 @@ export const createFor = ( } } + if (setup) { + setup({ createSelector }) + } + if (flags & VaporVForFlags.ONCE) { renderList() } else { @@ -412,12 +419,9 @@ export const createFor = ( insert(frag, _insertionParent, _insertionAnchor) } - // @ts-expect-error - frag.useSelector = useSelector - return frag - function useSelector(source: () => any): (key: any, cb: () => void) => void { + function createSelector(source: () => any): (cb: () => void) => void { let operMap = new Map void)[]>() let activeKey = source() let activeOpers: (() => void)[] | undefined