}"
`;
+exports[`compiler: codegen CacheExpression 1`] = `
+"
+export default function render() {
+ const _ctx = this
+ const _cache = _ctx.$cache
+ return _cache[1] || (_cache[1] = foo)
+}"
+`;
+
exports[`compiler: codegen ConditionalExpression 1`] = `
"
return function render() {
expect(code).toMatchSnapshot()
})
- test('cached', () => {
- const root = createRoot({ cached: 3 })
- const { code } = generate(root)
- expect(code).toMatch(`let _cached_1, _cached_2, _cached_3`)
- })
-
test('prefixIdentifiers: true should inject _ctx statement', () => {
const { code } = generate(createRoot(), { prefixIdentifiers: true })
expect(code).toMatch(`const _ctx = this\n`)
test('CacheExpression', () => {
const { code } = generate(
createRoot({
+ cached: 1,
codegenNode: createCacheExpression(
1,
createSimpleExpression(`foo`, false)
)
- })
+ }),
+ {
+ mode: 'module',
+ prefixIdentifiers: true
+ }
)
- expect(code).toMatch(`_cached_1 || (_cached_1 = foo)`)
+ expect(code).toMatch(`const _cache = _ctx.$cache`)
+ expect(code).toMatch(`_cache[1] || (_cache[1] = foo)`)
+ expect(code).toMatchSnapshot()
})
})
`;
exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist elements with cached handlers 1`] = `
-"const _Vue = Vue
-
-let _cached_1
-
-return function render() {
- with (this) {
- const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
-
- return (_openBlock(), _createBlock(\\"div\\", null, [
- _createVNode(\\"div\\", {
- onClick: _cached_1 || (_cached_1 = $event => (_ctx.foo($event)))
+"import { createVNode, createBlock, openBlock } from \\"vue\\"
+
+export default function render() {
+ const _ctx = this
+ const _cache = _ctx.$cache
+ return (openBlock(), createBlock(\\"div\\", null, [
+ createVNode(\\"div\\", null, [
+ createVNode(\\"div\\", {
+ onClick: _cache[1] || (_cache[1] = $event => (_ctx.foo($event)))
})
- ]))
- }
+ ])
+ ]))
}"
`;
})
test('should NOT hoist elements with cached handlers', () => {
- const { root } = transformWithHoist(`<div><div @click="foo"/></div>`, {
- prefixIdentifiers: true,
- cacheHandlers: true
- })
+ const { root } = transformWithHoist(
+ `<div><div><div @click="foo"/></div></div>`,
+ {
+ prefixIdentifiers: true,
+ cacheHandlers: true
+ }
+ )
expect(root.cached).toBe(1)
expect(root.hoists.length).toBe(0)
- expect(generate(root).code).toMatchSnapshot()
+ expect(
+ generate(root, {
+ mode: 'module',
+ prefixIdentifiers: true
+ }).code
+ ).toMatchSnapshot()
})
})
})
}
}
genHoists(ast.hoists, context)
- genCached(ast.cached, context)
newline()
push(`return `)
} else {
push(`import { ${ast.helpers.map(helper).join(', ')} } from "vue"\n`)
}
genHoists(ast.hoists, context)
- genCached(ast.cached, context)
newline()
push(`export default `)
}
}
} else {
push(`const _ctx = this`)
+ if (ast.cached > 0) {
+ newline()
+ push(`const _cache = _ctx.$cache`)
+ }
newline()
}
})
}
-function genCached(cached: number, context: CodegenContext) {
- if (cached > 0) {
- context.newline()
- context.push(`let `)
- for (let i = 0; i < cached; i++) {
- context.push(`_cached_${i + 1}`)
- if (i !== cached - 1) context.push(`, `)
- }
- context.newline()
- }
-}
-
function isText(n: string | CodegenNode) {
return (
isString(n) ||
}
function genCacheExpression(node: CacheExpression, context: CodegenContext) {
- context.push(`_cached_${node.index} || (_cached_${node.index} = `)
+ context.push(`_cache[${node.index}] || (_cache[${node.index}] = `)
genNode(node.value, context)
context.push(`)`)
}
)
},
cache(exp) {
- if (cacheHandlers) {
- context.cached++
- return createCacheExpression(context.cached, exp)
- } else {
- return exp
- }
+ return cacheHandlers ? createCacheExpression(++context.cached, exp) : exp
}
}
child.type === NodeTypes.ELEMENT &&
child.tagType === ElementTypes.ELEMENT
) {
- const hasBailoutProp = hasDynamicKeyOrRef(child) || hasCachedProps(child)
- if (
- !doNotHoistNode &&
- !hasBailoutProp &&
- isStaticNode(child, resultCache)
- ) {
+ if (!doNotHoistNode && isStaticNode(child, resultCache)) {
// whole tree is static
child.codegenNode = context.hoist(child.codegenNode!)
continue
(!flag ||
flag === PatchFlags.NEED_PATCH ||
flag === PatchFlags.TEXT) &&
- !hasBailoutProp
+ !hasDynamicKeyOrRef(child) &&
+ !hasCachedProps(child)
) {
const props = getNodeProps(child)
if (props && props !== `null`) {
return cached
}
const flag = getPatchFlag(node)
- if (!flag) {
+ if (!flag && !hasDynamicKeyOrRef(node) && !hasCachedProps(node)) {
// element self is static. check its children.
for (let i = 0; i < node.children.length; i++) {
if (!isStaticNode(node.children[i], resultCache)) {
render: RenderFunction | null
effects: ReactiveEffect[] | null
provides: Data
- accessCache: Data
+ // cache for renderProxy access type to avoid hasOwnProperty calls
+ accessCache: Data | null
+ // cache for render function values that rely on _ctx but won't need updates
+ // after initialized (e.g. inline handlers)
+ renderCache: any[] | null
components: Record<string, Component>
directives: Record<string, Directive>
effects: null,
provides: parent ? parent.provides : Object.create(appContext.provides),
accessCache: null!,
+ renderCache: null,
// setup context properties
renderContext: EMPTY_OBJ,
// is the multiple hasOwn() calls. It's much faster to do a simple property
// access on a plain object, so we use an accessCache object (with null
// prototype) to memoize what access type a key corresponds to.
- const n = accessCache[key]
+ const n = accessCache![key]
if (n !== undefined) {
switch (n) {
case AccessTypes.DATA:
return propsProxy![key]
}
} else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
- accessCache[key] = AccessTypes.DATA
+ accessCache![key] = AccessTypes.DATA
return data[key]
} else if (hasOwn(renderContext, key)) {
- accessCache[key] = AccessTypes.CONTEXT
+ accessCache![key] = AccessTypes.CONTEXT
return renderContext[key]
} else if (hasOwn(props, key)) {
// only cache props access if component has declared (thus stable) props
if (type.props != null) {
- accessCache[key] = AccessTypes.PROPS
+ accessCache![key] = AccessTypes.PROPS
}
// return the value from propsProxy for ref unwrapping and readonly
return propsProxy![key]
+ } else if (key === '$cache') {
+ return target.renderCache || (target.renderCache = [])
} else if (key === '$el') {
return target.vnode.el
} else if (hasOwn(publicPropertiesMap, key)) {