chore(compiler-core): reduce unnecessary cache inside v-once (#4112)

This commit is contained in:
fishDog 2021-07-16 04:50:54 +08:00 committed by Evan You
parent e5a4412764
commit eca4d7891a
7 changed files with 37 additions and 2 deletions

View File

@ -425,6 +425,18 @@ describe('compiler: transform v-model', () => {
).not.toBe(NodeTypes.JS_CACHE_EXPRESSION) ).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', () => { test('should mark update handler dynamic if it refers slot scope variables', () => {
const root = parseWithVModel( const root = parseWithVModel(
'<Comp v-slot="{ foo }"><input v-model="foo.bar"/></Comp>', '<Comp v-slot="{ foo }"><input v-model="foo.bar"/></Comp>',

View File

@ -530,6 +530,15 @@ describe('compiler: transform v-on', () => {
expect(root.cached).toBe(0) 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', () => { test('inline function expression handler', () => {
const { root, node } = parseWithVOn(`<div v-on:click="() => foo()" />`, { const { root, node } = parseWithVOn(`<div v-on:click="() => foo()" />`, {
prefixIdentifiers: true, prefixIdentifiers: true,

View File

@ -80,6 +80,13 @@ describe('compiler: v-once transform', () => {
expect(generate(root).code).toMatchSnapshot() 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 // cached nodes should be ignored by hoistStatic transform
test('with hoistStatic: true', () => { test('with hoistStatic: true', () => {
const root = transformWithOnce(`<div><div v-once /></div>`, { const root = transformWithOnce(`<div><div v-once /></div>`, {

View File

@ -105,6 +105,7 @@ export interface TransformContext
parent: ParentNode | null parent: ParentNode | null
childIndex: number childIndex: number
currentNode: RootNode | TemplateChildNode | null currentNode: RootNode | TemplateChildNode | null
inVOnce: boolean
helper<T extends symbol>(name: T): T helper<T extends symbol>(name: T): T
removeHelper<T extends symbol>(name: T): void removeHelper<T extends symbol>(name: T): void
helperString(name: symbol): string helperString(name: symbol): string
@ -192,6 +193,7 @@ export function createTransformContext(
parent: null, parent: null,
currentNode: root, currentNode: root,
childIndex: 0, childIndex: 0,
inVOnce: false,
// methods // methods
helper(name) { helper(name) {

View File

@ -107,6 +107,7 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
if ( if (
!__BROWSER__ && !__BROWSER__ &&
context.prefixIdentifiers && context.prefixIdentifiers &&
!context.inVOnce &&
context.cacheHandlers && context.cacheHandlers &&
!hasScopeRef(exp, context.identifiers) !hasScopeRef(exp, context.identifiers)
) { ) {

View File

@ -70,7 +70,7 @@ export const transformOn: DirectiveTransform = (
if (exp && !exp.content.trim()) { if (exp && !exp.content.trim()) {
exp = undefined exp = undefined
} }
let shouldCache: boolean = context.cacheHandlers && !exp let shouldCache: boolean = context.cacheHandlers && !exp && !context.inVOnce
if (exp) { if (exp) {
const isMemberExp = isMemberExpression(exp.content) const isMemberExp = isMemberExpression(exp.content)
const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content)) const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content))
@ -90,6 +90,8 @@ export const transformOn: DirectiveTransform = (
// to scope variables. // to scope variables.
shouldCache = shouldCache =
context.cacheHandlers && context.cacheHandlers &&
// unnecessary to cache inside v-once
!context.inVOnce &&
// runtime constants don't need to be cached // runtime constants don't need to be cached
// (this is analyzed by compileScript in SFC <script setup>) // (this is analyzed by compileScript in SFC <script setup>)
!(exp.type === NodeTypes.SIMPLE_EXPRESSION && exp.constType > 0) && !(exp.type === NodeTypes.SIMPLE_EXPRESSION && exp.constType > 0) &&

View File

@ -7,12 +7,14 @@ const seen = new WeakSet()
export const transformOnce: NodeTransform = (node, context) => { export const transformOnce: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT && findDir(node, 'once', true)) { if (node.type === NodeTypes.ELEMENT && findDir(node, 'once', true)) {
if (seen.has(node)) { if (seen.has(node) || context.inVOnce) {
return return
} }
seen.add(node) seen.add(node)
context.inVOnce = true
context.helper(SET_BLOCK_TRACKING) context.helper(SET_BLOCK_TRACKING)
return () => { return () => {
context.inVOnce = false
const cur = context.currentNode as ElementNode | IfNode | ForNode const cur = context.currentNode as ElementNode | IfNode | ForNode
if (cur.codegenNode) { if (cur.codegenNode) {
cur.codegenNode = context.cache(cur.codegenNode, true /* isVNode */) cur.codegenNode = context.cache(cur.codegenNode, true /* isVNode */)