).not.toBe(NodeTypes.JS_CACHE_EXPRESSION)
})
+ test('should not cache update handler if it inside v-once', () => {
+ const root = parseWithVModel(
+ '<div v-once><input v-model="foo" /></div>',
+ {
+ prefixIdentifiers: true,
+ cacheHandlers: true
+ }
+ )
+ expect(root.cached).not.toBe(2)
+ expect(root.cached).toBe(1)
+ })
+
test('should mark update handler dynamic if it refers slot scope variables', () => {
const root = parseWithVModel(
'<Comp v-slot="{ foo }"><input v-model="foo.bar"/></Comp>',
expect(root.cached).toBe(0)
})
+ test('should not be cached inside v-once', () => {
+ const { root } = parseWithVOn(`<div v-once><div v-on:click="foo"/></div>`, {
+ prefixIdentifiers: true,
+ cacheHandlers: true
+ })
+ expect(root.cached).not.toBe(2)
+ expect(root.cached).toBe(1)
+ })
+
test('inline function expression handler', () => {
const { root, node } = parseWithVOn(`<div v-on:click="() => foo()" />`, {
prefixIdentifiers: true,
expect(generate(root).code).toMatchSnapshot()
})
+ // v-once inside v-once should not be cached
+ test('inside v-once', () => {
+ const root = transformWithOnce(`<div v-once><div v-once/></div>`)
+ expect(root.cached).not.toBe(2)
+ expect(root.cached).toBe(1)
+ })
+
// cached nodes should be ignored by hoistStatic transform
test('with hoistStatic: true', () => {
const root = transformWithOnce(`<div><div v-once /></div>`, {
parent: ParentNode | null
childIndex: number
currentNode: RootNode | TemplateChildNode | null
+ inVOnce: boolean
helper<T extends symbol>(name: T): T
removeHelper<T extends symbol>(name: T): void
helperString(name: symbol): string
parent: null,
currentNode: root,
childIndex: 0,
+ inVOnce: false,
// methods
helper(name) {
if (
!__BROWSER__ &&
context.prefixIdentifiers &&
+ !context.inVOnce &&
context.cacheHandlers &&
!hasScopeRef(exp, context.identifiers)
) {
if (exp && !exp.content.trim()) {
exp = undefined
}
- let shouldCache: boolean = context.cacheHandlers && !exp
+ let shouldCache: boolean = context.cacheHandlers && !exp && !context.inVOnce
if (exp) {
const isMemberExp = isMemberExpression(exp.content)
const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content))
// to scope variables.
shouldCache =
context.cacheHandlers &&
+ // unnecessary to cache inside v-once
+ !context.inVOnce &&
// runtime constants don't need to be cached
// (this is analyzed by compileScript in SFC <script setup>)
!(exp.type === NodeTypes.SIMPLE_EXPRESSION && exp.constType > 0) &&
export const transformOnce: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT && findDir(node, 'once', true)) {
- if (seen.has(node)) {
+ if (seen.has(node) || context.inVOnce) {
return
}
seen.add(node)
+ context.inVOnce = true
context.helper(SET_BLOCK_TRACKING)
return () => {
+ context.inVOnce = false
const cur = context.currentNode as ElementNode | IfNode | ForNode
if (cur.codegenNode) {
cur.codegenNode = context.cache(cur.codegenNode, true /* isVNode */)