]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-vapor): selectors was not initialized in time when the initial value...
authorJohnson Chu <johnsoncodehk@gmail.com>
Fri, 18 Jul 2025 07:00:04 +0000 (15:00 +0800)
committerGitHub <noreply@github.com>
Fri, 18 Jul 2025 07:00:04 +0000 (15:00 +0800)
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
packages/compiler-vapor/src/generators/for.ts
packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts
packages/runtime-vapor/src/apiCreateFor.ts

index 2d64e1ffe5288df6c5e97298468229f60da8af48..7184446fc09d0a7bd42ab12550f4892df3fbf955 100644 (file)
@@ -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
 }"
 `;
index 141d3e410dccd93473750a80a1272f95fb9c7d59..69c695a246a424ef1a2ca66d473d418854fc4016 100644 (file)
@@ -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("<tr> </tr>", 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("<tr></tr>", 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("<tr></tr>", 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
 }"
 `;
index ab3ade45b602fb013a66fa37922b53f2b2ee92bc..b6107d5a1a1c63067461c43cda989eb22e144065 100644 (file)
@@ -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
 }"
 `;
index 40f002a8536870d411ea4218beb649ce71dc6ea8..026db797ba615da02cad16ed93402a1a070aa247 100644 (file)
@@ -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.
index 9b36a2c311fd83b417d9508f4235eac626e81ca5..9a3745fb545ba9ead941bcbec0ed8e38f4c3a672 100644 (file)
@@ -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(
-      '<li>0.f</li><li>1.f</li><li>3.f</li><!--for-->',
-    )
-    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(
-      '<li>1.t</li><li>2.f</li><li>3.f</li><!--for-->',
-    )
-    expect(calledTimes).toBe((expectedCalledTimes += 3))
-
-    index.value = 1
-    await nextTick()
-    expect(host.innerHTML).toBe(
-      '<li>1.f</li><li>2.t</li><li>3.f</li><!--for-->',
-    )
-    expect(calledTimes).toBe((expectedCalledTimes += 2))
-
-    index.value = 2
-    await nextTick()
-    expect(host.innerHTML).toBe(
-      '<li>1.f</li><li>2.f</li><li>3.t</li><!--for-->',
-    )
-    expect(calledTimes).toBe((expectedCalledTimes += 2))
-
-    list.value[2].id = 4
-    await nextTick()
-    expect(host.innerHTML).toBe(
-      '<li>1.f</li><li>2.f</li><li>4.f</li><!--for-->',
-    )
-    expect(calledTimes).toBe((expectedCalledTimes += 1))
+    // list.value[2].id = 3
+    // await nextTick()
+    // expect(host.innerHTML).toBe(
+    //   '<li>0.f</li><li>1.f</li><li>3.f</li><!--for-->',
+    // )
+    // expect(calledTimes).toBe((expectedCalledTimes += 1))
   })
 })
index 426a5c56b5b1ba226f352a31985c524ee1b87cf7..9ffdf6dca571ec2217632f6e10dc85abc8c25095 100644 (file)
@@ -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<any, (() => void)[]>()
     let activeKey = source()
     let activeOpers: (() => void)[] | undefined