]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(v-once): fix v-once usage with v-if and v-for
authorEvan You <yyx990803@gmail.com>
Wed, 2 Sep 2020 16:29:07 +0000 (12:29 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 2 Sep 2020 16:30:37 +0000 (12:30 -0400)
fix #2035

packages/compiler-core/__tests__/transforms/vOnce.spec.ts
packages/compiler-core/src/transform.ts
packages/compiler-core/src/transforms/vOnce.ts
packages/vue/__tests__/index.spec.ts

index 45eff37d048ad2bad68c9780c8d3078672d0f998..3983aa9605a008434703d39dcc5756a15b23dba8 100644 (file)
@@ -3,21 +3,17 @@ import {
   transform,
   NodeTypes,
   generate,
-  CompilerOptions
+  CompilerOptions,
+  getBaseTransformPreset
 } from '../../src'
-import { transformOnce } from '../../src/transforms/vOnce'
-import { transformElement } from '../../src/transforms/transformElement'
 import { RENDER_SLOT, SET_BLOCK_TRACKING } from '../../src/runtimeHelpers'
-import { transformBind } from '../../src/transforms/vBind'
-import { transformSlotOutlet } from '../../src/transforms/transformSlotOutlet'
 
 function transformWithOnce(template: string, options: CompilerOptions = {}) {
   const ast = parse(template)
+  const [nodeTransforms, directiveTransforms] = getBaseTransformPreset()
   transform(ast, {
-    nodeTransforms: [transformOnce, transformElement, transformSlotOutlet],
-    directiveTransforms: {
-      bind: transformBind
-    },
+    nodeTransforms,
+    directiveTransforms,
     ...options
   })
   return ast
@@ -102,4 +98,30 @@ describe('compiler: v-once transform', () => {
     })
     expect(generate(root).code).toMatchSnapshot()
   })
+
+  test('with v-if', () => {
+    const root = transformWithOnce(`<div v-if="true" v-once />`)
+    expect(root.cached).toBe(1)
+    expect(root.helpers).toContain(SET_BLOCK_TRACKING)
+    expect(root.children[0]).toMatchObject({
+      type: NodeTypes.IF,
+      // should cache the entire v-if expression, not just a single branch
+      codegenNode: {
+        type: NodeTypes.JS_CACHE_EXPRESSION
+      }
+    })
+  })
+
+  test('with v-for', () => {
+    const root = transformWithOnce(`<div v-for="i in list" v-once />`)
+    expect(root.cached).toBe(1)
+    expect(root.helpers).toContain(SET_BLOCK_TRACKING)
+    expect(root.children[0]).toMatchObject({
+      type: NodeTypes.FOR,
+      // should cache the entire v-for expression, not just a single branch
+      codegenNode: {
+        type: NodeTypes.JS_CACHE_EXPRESSION
+      }
+    })
+  })
 })
index 05284751ab3baf6e8aec3b3db7772bc70f89b443..4495d498094a513860ac81666f27fe191b03354f 100644 (file)
@@ -399,6 +399,7 @@ export function traverseNode(
   }
 
   // exit transforms
+  context.currentNode = node
   let i = exitFns.length
   while (i--) {
     exitFns[i]()
index f9d46f1a1f15b137725edb3f7427878b2f3849f3..06c5171e1d83b5cc613cd94702fc0e08432d9afe 100644 (file)
@@ -1,14 +1,21 @@
 import { NodeTransform } from '../transform'
 import { findDir } from '../utils'
-import { NodeTypes } from '../ast'
+import { ElementNode, ForNode, IfNode, NodeTypes } from '../ast'
 import { SET_BLOCK_TRACKING } from '../runtimeHelpers'
 
+const seen = new WeakSet()
+
 export const transformOnce: NodeTransform = (node, context) => {
   if (node.type === NodeTypes.ELEMENT && findDir(node, 'once', true)) {
+    if (seen.has(node)) {
+      return
+    }
+    seen.add(node)
     context.helper(SET_BLOCK_TRACKING)
     return () => {
-      if (node.codegenNode) {
-        node.codegenNode = context.cache(node.codegenNode, true /* isVNode */)
+      const cur = context.currentNode as ElementNode | IfNode | ForNode
+      if (cur.codegenNode) {
+        cur.codegenNode = context.cache(cur.codegenNode, true /* isVNode */)
       }
     }
   }
index 7339a0804fa864238a94e0f780bac76668c342b7..2de237e2a1baaf96aec8dc62de3338fe679d8c82 100644 (file)
@@ -1,4 +1,4 @@
-import { createApp, ref, nextTick } from '../src'
+import { createApp, ref, nextTick, reactive } from '../src'
 
 describe('compiler + runtime integration', () => {
   it('should support runtime template compilation', () => {
@@ -247,4 +247,38 @@ describe('compiler + runtime integration', () => {
 
     document.querySelector = origin
   })
+
+  test('v-if + v-once', async () => {
+    const ok = ref(true)
+    const App = {
+      setup() {
+        return { ok }
+      },
+      template: `<div>{{ ok }}<div v-if="ok" v-once>{{ ok }}</div></div>`
+    }
+    const container = document.createElement('div')
+    createApp(App).mount(container)
+
+    expect(container.innerHTML).toBe(`<div>true<div>true</div></div>`)
+    ok.value = false
+    await nextTick()
+    expect(container.innerHTML).toBe(`<div>false<div>true</div></div>`)
+  })
+
+  test('v-for + v-once', async () => {
+    const list = reactive([1])
+    const App = {
+      setup() {
+        return { list }
+      },
+      template: `<div>{{ list.length }}<div v-for="i in list" v-once>{{ i }}</div></div>`
+    }
+    const container = document.createElement('div')
+    createApp(App).mount(container)
+
+    expect(container.innerHTML).toBe(`<div>1<div>1</div></div>`)
+    list.push(2)
+    await nextTick()
+    expect(container.innerHTML).toBe(`<div>2<div>1</div></div>`)
+  })
 })