-import { walkIdentifiers } from '@vue/compiler-dom'
+import { type SimpleExpressionNode, walkIdentifiers } from '@vue/compiler-dom'
import { genBlock } from './block'
import { genExpression } from './expression'
import type { CodegenContext } from '../generate'
context: CodegenContext,
): CodeFragment[] {
const { vaporHelper } = context
- const { source, value, key, index, render, keyProp, once, id } = oper
+ const { source, value, key, index, render, keyProp, once, id, memo } = oper
let isDestructureAssignment = false
let rawValue: string | null = null
const rawIndex = index && index.content
const sourceExpr = ['() => (', ...genExpression(source, context), ')']
-
- const idsOfValue = new Set<string>()
- if (value) {
- rawValue = value && value.content
- if ((isDestructureAssignment = !!value.ast)) {
- walkIdentifiers(
- value.ast,
- (id, _, __, ___, isLocal) => {
- if (isLocal) idsOfValue.add(id.name)
- },
- true,
- )
- } else {
- idsOfValue.add(rawValue)
- }
- }
-
- const [depth, exitScope] = context.enterScope()
- let propsName: string
- const idMap: Record<string, string | null> = {}
- if (context.options.prefixIdentifiers) {
- propsName = `_ctx${depth}`
- Array.from(idsOfValue).forEach(
- (id, idIndex) => (idMap[id] = `${propsName}[${idIndex}].value`),
- )
- if (rawKey) idMap[rawKey] = `${propsName}[${idsOfValue.size}].value`
- if (rawIndex) idMap[rawIndex] = `${propsName}[${idsOfValue.size + 1}].value`
- } else {
- propsName = `[${[rawValue || ((rawKey || rawIndex) && '_'), rawKey || (rawIndex && '__'), rawIndex].filter(Boolean).join(', ')}]`
- }
-
- let blockFn = context.withId(
- () => genBlock(render, context, [propsName]),
- idMap,
- )
- exitScope()
-
- let getKeyFn: CodeFragment[] | false = false
- if (keyProp) {
- const idMap: Record<string, null> = {}
- if (rawKey) idMap[rawKey] = null
- if (rawIndex) idMap[rawIndex] = null
- idsOfValue.forEach(id => (idMap[id] = null))
-
- const expr = context.withId(() => genExpression(keyProp, context), idMap)
- getKeyFn = [
- ...genMulti(
- ['(', ')', ', '],
- rawValue ? rawValue : rawKey || rawIndex ? '_' : undefined,
- rawKey ? rawKey : rawIndex ? '__' : undefined,
- rawIndex,
- ),
- ' => (',
- ...expr,
- ')',
- ]
- }
+ const idsInValue = getIdsInValue()
+ let blockFn = genBlockFn()
+ const simpleIdMap: Record<string, null> = genSimpleIdMap()
if (isDestructureAssignment) {
const idMap: Record<string, null> = {}
- idsOfValue.forEach(id => (idMap[id] = null))
+ idsInValue.forEach(id => (idMap[id] = null))
if (rawKey) idMap[rawKey] = null
if (rawIndex) idMap[rawIndex] = null
const destructureAssignmentFn: CodeFragment[] = [
rawIndex,
),
') => ',
- ...genMulti(DELIMITERS_ARRAY, ...idsOfValue, rawKey, rawIndex),
+ ...genMulti(DELIMITERS_ARRAY, ...idsInValue, rawKey, rawIndex),
]
blockFn = genCall(
vaporHelper('createFor'),
sourceExpr,
blockFn,
- getKeyFn,
- false, // todo: getMemo
+ genCallback(keyProp),
+ genCallback(memo),
false, // todo: hydrationNode
once && 'true',
),
]
+
+ function getIdsInValue() {
+ const idsInValue = new Set<string>()
+ if (value) {
+ rawValue = value && value.content
+ if ((isDestructureAssignment = !!value.ast)) {
+ walkIdentifiers(
+ value.ast,
+ (id, _, __, ___, isLocal) => {
+ if (isLocal) idsInValue.add(id.name)
+ },
+ true,
+ )
+ } else {
+ idsInValue.add(rawValue)
+ }
+ }
+ return idsInValue
+ }
+
+ function genBlockFn() {
+ const [depth, exitScope] = context.enterScope()
+ let propsName: string
+ const idMap: Record<string, string | null> = {}
+ if (context.options.prefixIdentifiers) {
+ propsName = `_ctx${depth}`
+ Array.from(idsInValue).forEach(
+ (id, idIndex) => (idMap[id] = `${propsName}[${idIndex}].value`),
+ )
+ if (rawKey) idMap[rawKey] = `${propsName}[${idsInValue.size}].value`
+ if (rawIndex)
+ idMap[rawIndex] = `${propsName}[${idsInValue.size + 1}].value`
+ } else {
+ propsName = `[${[rawValue || ((rawKey || rawIndex) && '_'), rawKey || (rawIndex && '__'), rawIndex].filter(Boolean).join(', ')}]`
+ }
+
+ const blockFn = context.withId(
+ () => genBlock(render, context, [propsName]),
+ idMap,
+ )
+ exitScope()
+ return blockFn
+ }
+
+ function genSimpleIdMap() {
+ const idMap: Record<string, null> = {}
+ if (rawKey) idMap[rawKey] = null
+ if (rawIndex) idMap[rawIndex] = null
+ idsInValue.forEach(id => (idMap[id] = null))
+ return idMap
+ }
+
+ function genCallback(expr: SimpleExpressionNode | undefined) {
+ if (!expr) return false
+ const res = context.withId(() => genExpression(expr, context), simpleIdMap)
+ return [
+ ...genMulti(
+ ['(', ')', ', '],
+ rawValue ? rawValue : rawKey || rawIndex ? '_' : undefined,
+ rawKey ? rawKey : rawIndex ? '__' : undefined,
+ rawIndex,
+ ),
+ ' => (',
+ ...res,
+ ')',
+ ]
+ }
}
import { componentKey } from './component'
import type { DynamicSlot } from './componentSlots'
import { renderEffect } from './renderEffect'
+import { withMemo } from './memo'
interface ForBlock extends Fragment {
scope: EffectScope
memo: getMemo && getMemo(item, key, index),
[fragmentKey]: true,
})
- block.nodes = scope.run(() => renderItem(state))!
+ block.nodes = scope.run(() => {
+ if (getMemo) {
+ return withMemo(
+ () => block.memo!,
+ () => renderItem(state),
+ )
+ }
+ return renderItem(state)
+ })!
// TODO v-memo
// if (getMemo) block.update()
}
}
- if (needsUpdate) setState(block, newItem, newKey, newIndex)
+ if (needsUpdate) updateState(block, newItem, newKey, newIndex)
}
function updateWithoutMemo(
newKey !== key.value ||
newIndex !== index.value ||
// shallowRef list
- (!isReactive(newItem) && isObject(newItem))
-
- if (needsUpdate) setState(block, newItem, newKey, newIndex)
+ (isObject(newItem) && !isReactive(newItem))
+ if (needsUpdate) updateState(block, newItem, newKey, newIndex)
}
function unmount({ nodes, scope }: ForBlock) {
}
}
-function setState(
+function updateState(
block: ForBlock,
newItem: any,
newKey: any,