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 da863b2407f..302e240ca50 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap
@@ -19,7 +19,7 @@ const t0 = _template("
", true)
export function render(_ctx) {
const _setTemplateRef = _createTemplateRefSetter()
- const n0 = _createFor(() => ([1,2,3]), (_ctx0) => {
+ const n0 = _createFor(() => ([1,2,3]), (_for_item0) => {
const n2 = t0()
_setTemplateRef(n2, "foo", void 0, true)
return n2
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 694b804cd4b..21b4b1d4a39 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap
@@ -5,9 +5,9 @@ exports[`compiler: v-for > array de-structured value (with rest) 1`] = `
const t0 = _template("", true)
export function render(_ctx) {
- const n0 = _createFor(() => (_ctx.list), (_ctx0) => {
+ const n0 = _createFor(() => (_ctx.list), (_for_item0, _for_key0) => {
const n2 = t0()
- _renderEffect(() => _setText(n2, _ctx0[0].value[0] + _ctx0[0].value.slice(1) + _ctx0[1].value))
+ _renderEffect(() => _setText(n2, _for_item0.value[0] + _for_item0.value.slice(1) + _for_key0.value))
return n2
}, ([id, ...other], index) => (id))
return n0
@@ -19,9 +19,9 @@ exports[`compiler: v-for > array de-structured value 1`] = `
const t0 = _template("", true)
export function render(_ctx) {
- const n0 = _createFor(() => (_ctx.list), (_ctx0) => {
+ const n0 = _createFor(() => (_ctx.list), (_for_item0, _for_key0) => {
const n2 = t0()
- _renderEffect(() => _setText(n2, _ctx0[0].value[0] + _ctx0[0].value[1] + _ctx0[1].value))
+ _renderEffect(() => _setText(n2, _for_item0.value[0] + _for_item0.value[1] + _for_key0.value))
return n2
}, ([id, other], index) => (id))
return n0
@@ -34,10 +34,10 @@ const t0 = _template("", true)
_delegateEvents("click")
export function render(_ctx) {
- const n0 = _createFor(() => (_ctx.items), (_ctx0) => {
+ const n0 = _createFor(() => (_ctx.items), (_for_item0) => {
const n2 = t0()
- _delegate(n2, "click", () => $event => (_ctx.remove(_ctx0[0].value)))
- _renderEffect(() => _setText(n2, _ctx0[0].value))
+ _delegate(n2, "click", () => $event => (_ctx.remove(_for_item0.value)))
+ _renderEffect(() => _setText(n2, _for_item0.value))
return n2
}, (item) => (item.id))
return n0
@@ -49,11 +49,11 @@ exports[`compiler: v-for > multi effect 1`] = `
const t0 = _template("", true)
export function render(_ctx) {
- const n0 = _createFor(() => (_ctx.items), (_ctx0) => {
+ const n0 = _createFor(() => (_ctx.items), (_for_item0, _for_key0) => {
const n2 = t0()
_renderEffect(() => {
- _setProp(n2, "item", _ctx0[0].value)
- _setProp(n2, "index", _ctx0[1].value)
+ _setProp(n2, "item", _for_item0.value)
+ _setProp(n2, "index", _for_key0.value)
})
return n2
})
@@ -67,11 +67,11 @@ const t0 = _template("")
const t1 = _template("", true)
export function render(_ctx) {
- const n0 = _createFor(() => (_ctx.list), (_ctx0) => {
+ const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
const n5 = t1()
- const n2 = _createFor(() => (_ctx0[0].value), (_ctx1) => {
+ const n2 = _createFor(() => (_for_item0.value), (_for_item1) => {
const n4 = t0()
- _renderEffect(() => _setText(n4, _ctx1[0].value+_ctx0[0].value))
+ _renderEffect(() => _setText(n4, _for_item1.value+_for_item0.value))
return n4
})
_insert(n2, n5)
@@ -86,9 +86,9 @@ exports[`compiler: v-for > object de-structured value (with rest) 1`] = `
const t0 = _template("", true)
export function render(_ctx) {
- const n0 = _createFor(() => (_ctx.list), (_ctx0) => {
+ const n0 = _createFor(() => (_ctx.list), (_for_item0, _for_key0) => {
const n2 = t0()
- _renderEffect(() => _setText(n2, _ctx0[0].value.id + _getRestElement(_ctx0[0].value, ["id"]) + _ctx0[1].value))
+ _renderEffect(() => _setText(n2, _for_item0.value.id + _getRestElement(_for_item0.value, ["id"]) + _for_key0.value))
return n2
}, ({ id, ...other }, index) => (id))
return n0
@@ -100,9 +100,9 @@ exports[`compiler: v-for > object de-structured value 1`] = `
const t0 = _template("", true)
export function render(_ctx) {
- const n0 = _createFor(() => (_ctx.items), (_ctx0) => {
+ const n0 = _createFor(() => (_ctx.items), (_for_item0) => {
const n2 = t0()
- _renderEffect(() => _setText(n2, _ctx0[0].value.id, _ctx0[0].value.value))
+ _renderEffect(() => _setText(n2, _for_item0.value.id, _for_item0.value.value))
return n2
}, ({ id, value }) => (id))
return n0
@@ -114,9 +114,9 @@ exports[`compiler: v-for > v-for aliases w/ complex expressions 1`] = `
const t0 = _template("", true)
export function render(_ctx) {
- const n0 = _createFor(() => (_ctx.list), (_ctx0) => {
+ const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
const n2 = t0()
- _renderEffect(() => _setText(n2, _getDefaultValue(_ctx._ctx0[0].value.foo, _ctx.bar) + _ctx.bar + _ctx.baz + _getDefaultValue(_ctx._ctx0[0].value.baz[0], _ctx.quux) + _ctx.quux))
+ _renderEffect(() => _setText(n2, _getDefaultValue(_for_item0.value.foo, _ctx.bar) + _ctx.bar + _ctx.baz + _getDefaultValue(_for_item0.value.baz[0], _ctx.quux) + _ctx.quux))
return n2
})
return n0
@@ -128,7 +128,7 @@ exports[`compiler: v-for > w/o value 1`] = `
const t0 = _template("item
", true)
export function render(_ctx) {
- const n0 = _createFor(() => (_ctx.items), (_ctx0) => {
+ const n0 = _createFor(() => (_ctx.items), (_for_item0) => {
const n2 = t0()
return n2
})
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 15fcd2182a8..1f5b6c63476 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
@@ -65,7 +65,7 @@ exports[`compiler: v-once > with v-for 1`] = `
const t0 = _template("", true)
export function render(_ctx) {
- const n0 = _createFor(() => (_ctx.list), (_ctx0) => {
+ const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
const n2 = t0()
return n2
}, null, null, true)
diff --git a/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts b/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts
index 0e2a7bd826e..22ded75aeb9 100644
--- a/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts
@@ -84,9 +84,11 @@ describe('compiler: v-for', () => {
`{{ j+i }}
`,
)
expect(code).matchSnapshot()
- expect(code).contains(`_createFor(() => (_ctx.list), (_ctx0) => {`)
- expect(code).contains(`_createFor(() => (_ctx0[0].value), (_ctx1) => {`)
- expect(code).contains(`_ctx1[0].value+_ctx0[0].value`)
+ expect(code).contains(`_createFor(() => (_ctx.list), (_for_item0) => {`)
+ expect(code).contains(
+ `_createFor(() => (_for_item0.value), (_for_item1) => {`,
+ )
+ expect(code).contains(`_for_item1.value+_for_item0.value`)
expect(ir.template).toEqual(['', ''])
expect(ir.block.operation).toMatchObject([
{
@@ -149,7 +151,7 @@ describe('compiler: v-for', () => {
`{{ id + other + index }}
`,
)
expect(code).matchSnapshot()
- expect(code).toContain('_getRestElement(_ctx0[0].value, ["id"])')
+ expect(code).toContain('_getRestElement(_for_item0.value, ["id"])')
expect(ir.block.operation[0]).toMatchObject({
type: IRNodeTypes.FOR,
source: {
@@ -212,7 +214,7 @@ describe('compiler: v-for', () => {
`{{ id + other + index }}
`,
)
expect(code).matchSnapshot()
- expect(code).toContain('_ctx0[0].value.slice(1)')
+ expect(code).toContain('_for_item0.value.slice(1)')
expect(ir.block.operation[0]).toMatchObject({
type: IRNodeTypes.FOR,
source: {
@@ -246,11 +248,9 @@ describe('compiler: v-for', () => {
`,
)
expect(code).matchSnapshot()
+ expect(code).toContain(`_getDefaultValue(_for_item0.value.foo, _ctx.bar)`)
expect(code).toContain(
- `_getDefaultValue(_ctx._ctx0[0].value.foo, _ctx.bar)`,
- )
- expect(code).toContain(
- `_getDefaultValue(_ctx._ctx0[0].value.baz[0], _ctx.quux)`,
+ `_getDefaultValue(_for_item0.value.baz[0], _ctx.quux)`,
)
expect(ir.block.operation[0]).toMatchObject({
type: IRNodeTypes.FOR,
diff --git a/packages/compiler-vapor/src/generators/for.ts b/packages/compiler-vapor/src/generators/for.ts
index c927aa8e4a6..12d21b35151 100644
--- a/packages/compiler-vapor/src/generators/for.ts
+++ b/packages/compiler-vapor/src/generators/for.ts
@@ -27,11 +27,13 @@ export function genFor(
const idToPathMap = parseValueDestructure()
const [depth, exitScope] = context.enterScope()
- const propsName = `_ctx${depth}`
const idMap: Record = {}
+ const itemVar = `_for_item${depth}`
+ idMap[itemVar] = null
+
idToPathMap.forEach((pathInfo, id) => {
- let path = `${propsName}[0].value${pathInfo ? pathInfo.path : ''}`
+ let path = `${itemVar}.value${pathInfo ? pathInfo.path : ''}`
if (pathInfo) {
if (pathInfo.helper) {
idMap[pathInfo.helper] = null
@@ -50,13 +52,22 @@ export function genFor(
idMap[id] = path
}
})
- if (rawKey) idMap[rawKey] = `${propsName}[1].value`
- if (rawIndex) idMap[rawIndex] = `${propsName}[2].value`
- const blockFn = context.withId(
- () => genBlock(render, context, [propsName]),
- idMap,
- )
+ const args = [itemVar]
+ if (rawKey) {
+ const keyVar = `_for_key${depth}`
+ args.push(`, ${keyVar}`)
+ idMap[rawKey] = `${keyVar}.value`
+ idMap[keyVar] = null
+ }
+ if (rawIndex) {
+ const indexVar = `_for_index${depth}`
+ args.push(`, ${indexVar}`)
+ idMap[rawIndex] = `${indexVar}.value`
+ idMap[indexVar] = null
+ }
+
+ const blockFn = context.withId(() => genBlock(render, context, args), idMap)
exitScope()
return [
diff --git a/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts b/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts
index 03bf9c0f780..9b36a2c311f 100644
--- a/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts
+++ b/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts
@@ -18,7 +18,7 @@ describe.todo('api: createSelector', () => {
const isSleected = createSelector(index)
return createFor(
() => list.value,
- ([item]) => {
+ item => {
const span = document.createElement('li')
renderEffect(() => {
calledTimes += 1
@@ -73,7 +73,7 @@ describe.todo('api: createSelector', () => {
)
return createFor(
() => list.value,
- ([item]) => {
+ item => {
const span = document.createElement('li')
renderEffect(() => {
calledTimes += 1
diff --git a/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts b/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts
index 46aeeeeee58..96a533551c1 100644
--- a/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts
+++ b/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts
@@ -422,7 +422,7 @@ describe('api: template ref', () => {
const n1 = t0()
const n2 = createFor(
() => list,
- state => {
+ item => {
const n1 = t1()
createTemplateRefSetter()(
n1 as Element,
@@ -431,7 +431,6 @@ describe('api: template ref', () => {
true,
)
renderEffect(() => {
- const [item] = state
setText(n1, item)
})
return n1
@@ -485,7 +484,7 @@ describe('api: template ref', () => {
const n1 = t0()
const n2 = createFor(
() => list,
- state => {
+ item => {
const n1 = t1()
createTemplateRefSetter()(
n1 as Element,
@@ -494,7 +493,6 @@ describe('api: template ref', () => {
true,
)
renderEffect(() => {
- const [item] = state
setText(n1, item)
})
return n1
@@ -546,7 +544,7 @@ describe('api: template ref', () => {
const n2 = n1!.nextSibling!
const n3 = createFor(
() => list.value,
- state => {
+ item => {
const n4 = t1()
createTemplateRefSetter()(
n4 as Element,
@@ -555,7 +553,6 @@ describe('api: template ref', () => {
true,
)
renderEffect(() => {
- const [item] = state
setText(n4, item)
})
return n4
diff --git a/packages/runtime-vapor/__tests__/for.spec.ts b/packages/runtime-vapor/__tests__/for.spec.ts
index 28233cbc9f5..7ba6023b1e9 100644
--- a/packages/runtime-vapor/__tests__/for.spec.ts
+++ b/packages/runtime-vapor/__tests__/for.spec.ts
@@ -19,14 +19,13 @@ describe('createFor', () => {
const { host } = define(() => {
const n1 = createFor(
() => list.value,
- state => {
+ (item, key, index) => {
const span = document.createElement('li')
renderEffect(() => {
- const [{ value: item }, { value: key }, { value: index }] = state
- span.innerHTML = `${key}. ${item.name}`
+ span.innerHTML = `${key.value}. ${item.value.name}`
// index should be undefined if source is not an object
- expect(index).toBe(undefined)
+ expect(index.value).toBe(undefined)
})
return span
},
@@ -85,11 +84,10 @@ describe('createFor', () => {
const { host } = define(() => {
const n1 = createFor(
() => count.value,
- state => {
+ (item, key, index) => {
const span = document.createElement('li')
renderEffect(() => {
- const [{ value: item }, { value: key }, index] = state
- span.innerHTML = `${key}. ${item}`
+ span.innerHTML = `${key.value}. ${item.value}`
// index should be undefined if source is not an object
expect(index.value).toBe(undefined)
@@ -130,12 +128,11 @@ describe('createFor', () => {
const { host } = define(() => {
const n1 = createFor(
() => data.value,
- state => {
+ (item, key, index) => {
const span = document.createElement('li')
renderEffect(() => {
- const [{ value: item }, { value: key }, { value: index }] = state
- span.innerHTML = `${key}${index}. ${item}`
- expect(index).not.toBe(undefined)
+ span.innerHTML = `${key.value}${index.value}. ${item.value}`
+ expect(index.value).not.toBe(undefined)
})
return span
},
@@ -197,17 +194,11 @@ describe('createFor', () => {
const { host } = define(() => {
const n1 = createFor(
() => list.value,
- state => {
+ (item, key, index) => {
const span = document.createElement('li')
renderEffect(() => {
- const [
- {
- value: { name },
- },
- { value: key },
- index,
- ] = state
- span.innerHTML = `${key}. ${name}`
+ // compiler rewrites { name } destructure to inline access
+ span.innerHTML = `${key.value}. ${item.value.name}`
// index should be undefined if source is not an object
expect(index.value).toBe(undefined)
})
@@ -275,14 +266,14 @@ describe('createFor', () => {
const { host } = define(() => {
const n1 = createFor(
() => list.value,
- state => {
+ (item, _key, index) => {
const span = document.createElement('li')
renderEffect(() => {
span.innerHTML = JSON.stringify(
- getRestElement(state[0].value, ['name']),
+ getRestElement(item.value, ['name']),
)
// index should be undefined if source is not an object
- expect(state[2].value).toBe(undefined)
+ expect(index.value).toBe(undefined)
})
return span
},
@@ -341,12 +332,12 @@ describe('createFor', () => {
const { host } = define(() => {
const n1 = createFor(
() => list.value,
- state => {
+ (item, _key, index) => {
const span = document.createElement('li')
renderEffect(() => {
- span.innerHTML = getDefaultValue(state[0].value.x, '0')
+ span.innerHTML = getDefaultValue(item.value.x, '0')
// index should be undefined if source is not an object
- expect(state[2].value).toBe(undefined)
+ expect(index.value).toBe(undefined)
})
return span
},
@@ -378,14 +369,13 @@ describe('createFor', () => {
const { host } = define(() => {
const n1 = createFor(
() => list.value,
- state => {
+ (item, key, index) => {
const span = document.createElement('li')
renderEffect(() => {
- const [{ value: item }, { value: key }, { value: index }] = state
- span.innerHTML = `${key}. ${item.name}`
+ span.innerHTML = `${key.value}. ${item.value.name}`
// index should be undefined if source is not an object
- expect(index).toBe(undefined)
+ expect(index.value).toBe(undefined)
})
return span
},
@@ -485,7 +475,7 @@ describe('createFor', () => {
define(() => {
const n1 = createFor(
() => (++sourceCalledTimes, list.value),
- ([item, index]) => {
+ (item, index) => {
++renderCalledTimes
const span = document.createElement('li')
renderEffect(() => {
@@ -604,7 +594,7 @@ describe('createFor', () => {
define(() => {
const n1 = createFor(
() => (++sourceCalledTimes, list.value),
- ([item, index]) => {
+ (item, index) => {
++renderCalledTimes
const span = document.createElement('li')
renderEffect(() => {
diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts
index 50e34673fb1..8f8e62c30b3 100644
--- a/packages/runtime-vapor/src/apiCreateFor.ts
+++ b/packages/runtime-vapor/src/apiCreateFor.ts
@@ -15,27 +15,28 @@ import { currentInstance, isVaporComponent } from './component'
import type { DynamicSlot } from './componentSlots'
import { renderEffect } from './renderEffect'
-type ForBlockState = [
- item: ShallowRef,
- key: ShallowRef,
- index: ShallowRef,
-]
-
class ForBlock extends Fragment {
scope: EffectScope | undefined
- state: ForBlockState
key: any
+ itemRef: ShallowRef
+ keyRef: ShallowRef | undefined
+ indexRef: ShallowRef | undefined
+
constructor(
nodes: Block,
scope: EffectScope | undefined,
- state: ForBlockState,
- key: any,
+ item: ShallowRef,
+ key: ShallowRef | undefined,
+ index: ShallowRef | undefined,
+ renderKey: any,
) {
super(nodes)
this.scope = scope
- this.state = state
- this.key = key
+ this.itemRef = item
+ this.keyRef = key
+ this.indexRef = index
+ this.key = renderKey
}
}
@@ -50,7 +51,11 @@ type ResolvedSource = {
/*! #__NO_SIDE_EFFECTS__ */
export const createFor = (
src: () => Source,
- renderItem: (block: ForBlock['state']) => Block,
+ renderItem: (
+ item: ShallowRef,
+ key: ShallowRef,
+ index: ShallowRef,
+ ) => Block,
getKey?: (item: any, key: any, index?: number) => any,
/**
* Whether this v-for is used directly on a component. If true, we can avoid
@@ -261,32 +266,38 @@ export const createFor = (
}
}
+ const needKey = renderItem.length > 1
+ const needIndex = renderItem.length > 2
+
const mount = (
source: ResolvedSource,
idx: number,
anchor: Node | undefined = parentAnchor,
): ForBlock => {
const [item, key, index] = getItem(source, idx)
- const state = [
- shallowRef(item),
- shallowRef(key),
- shallowRef(index),
- ] as ForBlock['state']
+ const itemRef = shallowRef(item)
+ // avoid creating refs if the render fn doesn't need it
+ const keyRef = needKey ? shallowRef(key) : undefined
+ const indexRef = needIndex ? shallowRef(index) : undefined
let nodes: Block
let scope: EffectScope | undefined
if (isComponent) {
// component already has its own scope so no outer scope needed
- nodes = renderItem(state)
+ nodes = renderItem(itemRef, keyRef as any, indexRef as any)
} else {
scope = new EffectScope()
- nodes = scope.run(() => renderItem(state))!
+ nodes = scope.run(() =>
+ renderItem(itemRef, keyRef as any, indexRef as any),
+ )!
}
const block = (newBlocks[idx] = new ForBlock(
nodes,
scope,
- state,
+ itemRef,
+ keyRef,
+ indexRef,
getKey && getKey(item, key, index),
))
@@ -305,20 +316,19 @@ export const createFor = (
}
const update = (
- block: ForBlock,
+ { itemRef, keyRef, indexRef }: ForBlock,
newItem: any,
- newKey = block.state[1].value,
- newIndex = block.state[2].value,
+ newKey?: any,
+ newIndex?: any,
) => {
- const [item, key, index] = block.state
- if (
- newItem !== item.value ||
- newKey !== key.value ||
- newIndex !== index.value
- ) {
- item.value = newItem
- key.value = newKey
- index.value = newIndex
+ if (newIndex !== itemRef.value) {
+ itemRef.value = newItem
+ }
+ if (keyRef && newKey !== undefined && newKey !== keyRef.value) {
+ keyRef.value = newKey
+ }
+ if (indexRef && newIndex !== undefined && newIndex !== indexRef.value) {
+ indexRef.value = newIndex
}
}