feat(compiler-vapor): add getKey function for v-for

This commit is contained in:
三咲智子 Kevin Deng 2024-02-08 20:28:10 +08:00
parent 8fb01504da
commit 35b78920c4
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
8 changed files with 69 additions and 22 deletions

View File

@ -14,7 +14,7 @@ export function render(_ctx) {
_setText(n3, item)
})
return n2
})
}, (item) => (item.id))
return [n1]
}"
`;

View File

@ -21,7 +21,7 @@ const compileWithVFor = makeCompile({
describe('compiler: v-for', () => {
test('basic v-for', () => {
const { code, ir, vaporHelpers, helpers } = compileWithVFor(
`<div v-for="item of items" @click="remove(item)">{{ item }}</div>`,
`<div v-for="item of items" :key="item.id" @click="remove(item)">{{ item }}</div>`,
)
expect(code).matchSnapshot()
@ -52,6 +52,10 @@ describe('compiler: v-for', () => {
type: IRNodeTypes.BLOCK_FUNCTION,
templateIndex: 0,
},
keyProperty: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'item.id',
},
},
])
expect(ir.returns).toEqual([1])

View File

@ -17,7 +17,7 @@ export function genFor(
context: CodegenContext,
): CodeFragment[] {
const { call, vaporHelper } = context
const { source, value, key, render } = oper
const { source, value, key, render, keyProperty } = oper
const rawValue = value && value.content
const rawKey = key && key.content
@ -34,12 +34,31 @@ export function genFor(
idMap,
)
let getKeyFn: CodeFragment[] | false = false
if (keyProperty) {
const idMap: Record<string, null> = {}
if (rawValue) idMap[rawValue] = null
if (rawKey) idMap[rawKey] = null
const expr = context.withId(
() => genExpression(keyProperty, context),
idMap,
)
getKeyFn = [
'(',
rawValue ? rawValue : rawKey ? '_' : '',
rawKey && `, ${rawKey}`,
') => (',
...expr,
')',
]
}
context.genEffect = undefined
return [
NEWLINE,
`const n${oper.id} = `,
...call(vaporHelper('createFor'), sourceExpr, blockFn),
...call(vaporHelper('createFor'), sourceExpr, blockFn, getKeyFn),
]
function genEffectInFor(effects: IREffect[]): CodeFragment[] {

View File

@ -76,6 +76,7 @@ export interface ForIRNode extends BaseIRNode {
value?: SimpleExpressionNode
key?: SimpleExpressionNode
index?: SimpleExpressionNode
keyProperty?: SimpleExpressionNode
render: BlockFunctionIRNode
}

View File

@ -1,20 +1,16 @@
import {
type AttributeNode,
NodeTypes,
type SimpleExpressionNode,
createSimpleExpression,
findProp,
} from '@vue/compiler-dom'
import { EMPTY_EXPRESSION, type NodeTransform } from '../transform'
import { IRNodeTypes, type VaporDirectiveNode } from '../ir'
import { IRNodeTypes } from '../ir'
import { normalizeBindShorthand } from './vBind'
import { findProp } from '../utils'
export const transformRef: NodeTransform = (node, context) => {
if (node.type !== NodeTypes.ELEMENT) return
const dir = findProp(node, 'ref', false, true) as
| VaporDirectiveNode
| AttributeNode
const dir = findProp(node, 'ref', false, true)
if (!dir) return
let value: SimpleExpressionNode

View File

@ -35,19 +35,8 @@ export const transformVBind: DirectiveTransform = (dir, node, context) => {
let { exp } = dir
const arg = dir.arg!
if (arg.isStatic && isReservedProp(arg.content)) return
if (!exp) exp = normalizeBindShorthand(arg, context)
let camel = false
if (modifiers.includes('camel')) {
if (arg.isStatic) {
arg.content = camelize(arg.content)
} else {
camel = true
}
}
if (!exp.content.trim()) {
if (!__BROWSER__) {
// #10280 only error against empty expression in non-browser build
@ -60,6 +49,16 @@ export const transformVBind: DirectiveTransform = (dir, node, context) => {
exp = createSimpleExpression('', true, loc)
}
if (arg.isStatic && isReservedProp(arg.content)) return
let camel = false
if (modifiers.includes('camel')) {
if (arg.isStatic) {
arg.content = camelize(arg.content)
} else {
camel = true
}
}
return {
key: arg,
value: exp,

View File

@ -18,6 +18,7 @@ import {
type VaporDirectiveNode,
} from '../ir'
import { extend } from '@vue/shared'
import { findProp, propToExpression } from '../utils'
export const transformVFor = createStructuralDirectiveTransform(
'for',
@ -45,6 +46,8 @@ export function processFor(
const { source, value, key, index } = parseResult
const keyProp = findProp(node, 'key')
const keyProperty = keyProp && propToExpression(keyProp)
context.node = node = wrapTemplate(node, ['for'])
context.dynamic.flags |= DynamicFlag.NON_TEMPLATE | DynamicFlag.INSERT
const id = context.reference()
@ -71,6 +74,7 @@ export function processFor(
value: value as SimpleExpressionNode | undefined,
key: key as SimpleExpressionNode | undefined,
index: index as SimpleExpressionNode | undefined,
keyProperty,
render,
})
}

View File

@ -0,0 +1,24 @@
import {
type AttributeNode,
type ElementNode,
NodeTypes,
findProp as _findProp,
createSimpleExpression,
} from '@vue/compiler-dom'
import type { VaporDirectiveNode } from './ir'
import { EMPTY_EXPRESSION } from './transform'
export const findProp = _findProp as (
node: ElementNode,
name: string,
dynamicOnly?: boolean,
allowEmpty?: boolean,
) => AttributeNode | VaporDirectiveNode | undefined
export function propToExpression(prop: AttributeNode | VaporDirectiveNode) {
return prop.type === NodeTypes.ATTRIBUTE
? prop.value
? createSimpleExpression(prop.value.content, true, prop.value.loc)
: EMPTY_EXPRESSION
: prop.exp
}