feat: v-memo
This commit is contained in:
@@ -504,8 +504,8 @@ export function buildProps(
|
||||
}
|
||||
continue
|
||||
}
|
||||
// skip v-once - it is handled by its dedicated transform.
|
||||
if (name === 'once') {
|
||||
// skip v-once/v-memo - they are handled by dedicated transforms.
|
||||
if (name === 'once' || name === 'memo') {
|
||||
continue
|
||||
}
|
||||
// skip v-is and :is on <component>
|
||||
|
||||
@@ -24,7 +24,9 @@ import {
|
||||
ForRenderListExpression,
|
||||
BlockCodegenNode,
|
||||
ForIteratorExpression,
|
||||
ConstantTypes
|
||||
ConstantTypes,
|
||||
createBlockStatement,
|
||||
createCompoundExpression
|
||||
} from '../ast'
|
||||
import { createCompilerError, ErrorCodes } from '../errors'
|
||||
import {
|
||||
@@ -34,9 +36,15 @@ import {
|
||||
isSlotOutlet,
|
||||
injectProp,
|
||||
getVNodeBlockHelper,
|
||||
getVNodeHelper
|
||||
getVNodeHelper,
|
||||
findDir
|
||||
} from '../utils'
|
||||
import { RENDER_LIST, OPEN_BLOCK, FRAGMENT } from '../runtimeHelpers'
|
||||
import {
|
||||
RENDER_LIST,
|
||||
OPEN_BLOCK,
|
||||
FRAGMENT,
|
||||
IS_MEMO_SAME
|
||||
} from '../runtimeHelpers'
|
||||
import { processExpression } from './transformExpression'
|
||||
import { validateBrowserExpression } from '../validateExpression'
|
||||
import { PatchFlags, PatchFlagNames } from '@vue/shared'
|
||||
@@ -51,15 +59,14 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
const renderExp = createCallExpression(helper(RENDER_LIST), [
|
||||
forNode.source
|
||||
]) as ForRenderListExpression
|
||||
const memo = findDir(node, 'memo')
|
||||
const keyProp = findProp(node, `key`)
|
||||
const keyProperty = keyProp
|
||||
? createObjectProperty(
|
||||
`key`,
|
||||
keyProp.type === NodeTypes.ATTRIBUTE
|
||||
? createSimpleExpression(keyProp.value!.content, true)
|
||||
: keyProp.exp!
|
||||
)
|
||||
: null
|
||||
const keyExp =
|
||||
keyProp &&
|
||||
(keyProp.type === NodeTypes.ATTRIBUTE
|
||||
? createSimpleExpression(keyProp.value!.content, true)
|
||||
: keyProp.exp!)
|
||||
const keyProperty = keyProp ? createObjectProperty(`key`, keyExp!) : null
|
||||
|
||||
if (!__BROWSER__ && context.prefixIdentifiers && keyProperty) {
|
||||
// #2085 process :key expression needs to be processed in order for it
|
||||
@@ -189,11 +196,37 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
}
|
||||
}
|
||||
|
||||
renderExp.arguments.push(createFunctionExpression(
|
||||
createForLoopParams(forNode.parseResult),
|
||||
childBlock,
|
||||
true /* force newline */
|
||||
) as ForIteratorExpression)
|
||||
if (memo) {
|
||||
const loop = createFunctionExpression(
|
||||
createForLoopParams(forNode.parseResult, [
|
||||
createSimpleExpression(`_cached`)
|
||||
])
|
||||
)
|
||||
loop.body = createBlockStatement([
|
||||
createCompoundExpression([`const _memo = (`, memo.exp!, `)`]),
|
||||
createCompoundExpression([
|
||||
`if (_cached`,
|
||||
...(keyExp ? [` && _cached.key === `, keyExp] : []),
|
||||
` && ${context.helperString(
|
||||
IS_MEMO_SAME
|
||||
)}(_cached.memo, _memo)) return _cached`
|
||||
]),
|
||||
createCompoundExpression([`const _item = `, childBlock as any]),
|
||||
createSimpleExpression(`_item.memo = _memo`),
|
||||
createSimpleExpression(`return _item`)
|
||||
])
|
||||
renderExp.arguments.push(
|
||||
loop as ForIteratorExpression,
|
||||
createSimpleExpression(`_cache`),
|
||||
createSimpleExpression(String(context.cached++))
|
||||
)
|
||||
} else {
|
||||
renderExp.arguments.push(createFunctionExpression(
|
||||
createForLoopParams(forNode.parseResult),
|
||||
childBlock,
|
||||
true /* force newline */
|
||||
) as ForIteratorExpression)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -393,29 +426,21 @@ function createAliasExpression(
|
||||
)
|
||||
}
|
||||
|
||||
export function createForLoopParams({
|
||||
value,
|
||||
key,
|
||||
index
|
||||
}: ForParseResult): ExpressionNode[] {
|
||||
const params: ExpressionNode[] = []
|
||||
if (value) {
|
||||
params.push(value)
|
||||
}
|
||||
if (key) {
|
||||
if (!value) {
|
||||
params.push(createSimpleExpression(`_`, false))
|
||||
}
|
||||
params.push(key)
|
||||
}
|
||||
if (index) {
|
||||
if (!key) {
|
||||
if (!value) {
|
||||
params.push(createSimpleExpression(`_`, false))
|
||||
}
|
||||
params.push(createSimpleExpression(`__`, false))
|
||||
}
|
||||
params.push(index)
|
||||
}
|
||||
return params
|
||||
export function createForLoopParams(
|
||||
{ value, key, index }: ForParseResult,
|
||||
memoArgs: ExpressionNode[] = []
|
||||
): ExpressionNode[] {
|
||||
return createParamsList([value, key, index, ...memoArgs])
|
||||
}
|
||||
|
||||
function createParamsList(
|
||||
args: (ExpressionNode | undefined)[]
|
||||
): ExpressionNode[] {
|
||||
let i = args.length
|
||||
while (i--) {
|
||||
if (args[i]) break
|
||||
}
|
||||
return args
|
||||
.slice(0, i + 1)
|
||||
.map((arg, i) => arg || createSimpleExpression(`_`.repeat(i + 1), false))
|
||||
}
|
||||
|
||||
@@ -22,21 +22,22 @@ import {
|
||||
AttributeNode,
|
||||
locStub,
|
||||
CacheExpression,
|
||||
ConstantTypes
|
||||
ConstantTypes,
|
||||
MemoExpression
|
||||
} from '../ast'
|
||||
import { createCompilerError, ErrorCodes } from '../errors'
|
||||
import { processExpression } from './transformExpression'
|
||||
import { validateBrowserExpression } from '../validateExpression'
|
||||
import { FRAGMENT, CREATE_COMMENT, OPEN_BLOCK } from '../runtimeHelpers'
|
||||
import { FRAGMENT, CREATE_COMMENT } from '../runtimeHelpers'
|
||||
import {
|
||||
injectProp,
|
||||
findDir,
|
||||
findProp,
|
||||
isBuiltInType,
|
||||
getVNodeHelper,
|
||||
getVNodeBlockHelper
|
||||
makeBlock
|
||||
} from '../utils'
|
||||
import { PatchFlags, PatchFlagNames } from '@vue/shared'
|
||||
import { getMemoedVNodeCall } from '..'
|
||||
|
||||
export const transformIf = createStructuralDirectiveTransform(
|
||||
/^(if|else|else-if)$/,
|
||||
@@ -214,7 +215,7 @@ function createCodegenNodeForBranch(
|
||||
branch: IfBranchNode,
|
||||
keyIndex: number,
|
||||
context: TransformContext
|
||||
): IfConditionalExpression | BlockCodegenNode {
|
||||
): IfConditionalExpression | BlockCodegenNode | MemoExpression {
|
||||
if (branch.condition) {
|
||||
return createConditionalExpression(
|
||||
branch.condition,
|
||||
@@ -235,8 +236,8 @@ function createChildrenCodegenNode(
|
||||
branch: IfBranchNode,
|
||||
keyIndex: number,
|
||||
context: TransformContext
|
||||
): BlockCodegenNode {
|
||||
const { helper, removeHelper } = context
|
||||
): BlockCodegenNode | MemoExpression {
|
||||
const { helper } = context
|
||||
const keyProperty = createObjectProperty(
|
||||
`key`,
|
||||
createSimpleExpression(
|
||||
@@ -284,18 +285,17 @@ function createChildrenCodegenNode(
|
||||
)
|
||||
}
|
||||
} else {
|
||||
const vnodeCall = (firstChild as ElementNode)
|
||||
.codegenNode as BlockCodegenNode
|
||||
const ret = (firstChild as ElementNode).codegenNode as
|
||||
| BlockCodegenNode
|
||||
| MemoExpression
|
||||
const vnodeCall = getMemoedVNodeCall(ret)
|
||||
// Change createVNode to createBlock.
|
||||
if (vnodeCall.type === NodeTypes.VNODE_CALL && !vnodeCall.isBlock) {
|
||||
removeHelper(getVNodeHelper(context.inSSR, vnodeCall.isComponent))
|
||||
vnodeCall.isBlock = true
|
||||
helper(OPEN_BLOCK)
|
||||
helper(getVNodeBlockHelper(context.inSSR, vnodeCall.isComponent))
|
||||
if (vnodeCall.type === NodeTypes.VNODE_CALL) {
|
||||
makeBlock(vnodeCall, context)
|
||||
}
|
||||
// inject branch key
|
||||
injectProp(vnodeCall, keyProperty, context)
|
||||
return vnodeCall
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
40
packages/compiler-core/src/transforms/vMemo.ts
Normal file
40
packages/compiler-core/src/transforms/vMemo.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { NodeTransform } from '../transform'
|
||||
import { findDir, makeBlock } from '../utils'
|
||||
import {
|
||||
createCallExpression,
|
||||
createFunctionExpression,
|
||||
ElementTypes,
|
||||
MemoExpression,
|
||||
NodeTypes,
|
||||
PlainElementNode
|
||||
} from '../ast'
|
||||
import { WITH_MEMO } from '../runtimeHelpers'
|
||||
|
||||
const seen = new WeakSet()
|
||||
|
||||
export const transformMemo: NodeTransform = (node, context) => {
|
||||
if (node.type === NodeTypes.ELEMENT) {
|
||||
const dir = findDir(node, 'memo')
|
||||
if (!dir || seen.has(node)) {
|
||||
return
|
||||
}
|
||||
seen.add(node)
|
||||
return () => {
|
||||
const codegenNode =
|
||||
node.codegenNode ||
|
||||
(context.currentNode as PlainElementNode).codegenNode
|
||||
if (codegenNode && codegenNode.type === NodeTypes.VNODE_CALL) {
|
||||
// non-component sub tree should be turned into a block
|
||||
if (node.tagType !== ElementTypes.COMPONENT) {
|
||||
makeBlock(codegenNode, context)
|
||||
}
|
||||
node.codegenNode = createCallExpression(context.helper(WITH_MEMO), [
|
||||
dir.exp!,
|
||||
createFunctionExpression(undefined, codegenNode),
|
||||
`_cache`,
|
||||
String(context.cached++)
|
||||
]) as MemoExpression
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user