Просмотр исходного кода

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

fishDog 5 лет назад
Родитель
Сommit
eca4d7891a

+ 12 - 0
packages/compiler-core/__tests__/transforms/vModel.spec.ts

@@ -425,6 +425,18 @@ describe('compiler: transform v-model', () => {
     ).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>',

+ 9 - 0
packages/compiler-core/__tests__/transforms/vOn.spec.ts

@@ -530,6 +530,15 @@ describe('compiler: transform v-on', () => {
       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,

+ 7 - 0
packages/compiler-core/__tests__/transforms/vOnce.spec.ts

@@ -80,6 +80,13 @@ describe('compiler: v-once transform', () => {
     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>`, {

+ 2 - 0
packages/compiler-core/src/transform.ts

@@ -105,6 +105,7 @@ export interface TransformContext
   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
@@ -192,6 +193,7 @@ export function createTransformContext(
     parent: null,
     currentNode: root,
     childIndex: 0,
+    inVOnce: false,
 
     // methods
     helper(name) {

+ 1 - 0
packages/compiler-core/src/transforms/vModel.ts

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

+ 3 - 1
packages/compiler-core/src/transforms/vOn.ts

@@ -70,7 +70,7 @@ export const transformOn: DirectiveTransform = (
   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))
@@ -90,6 +90,8 @@ export const transformOn: DirectiveTransform = (
       // 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) &&

+ 3 - 1
packages/compiler-core/src/transforms/vOnce.ts

@@ -7,12 +7,14 @@ const seen = new WeakSet()
 
 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 */)