]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-core): do not generate TEXT flag if child is constant
authorEvan You <yyx990803@gmail.com>
Wed, 16 Oct 2019 16:00:55 +0000 (12:00 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 16 Oct 2019 16:00:55 +0000 (12:00 -0400)
packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap
packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts
packages/compiler-core/src/transforms/hoistStatic.ts
packages/compiler-core/src/transforms/transformElement.ts
packages/compiler-core/src/utils.ts

index 9ad4a155c2d146d2d195b4bc2638e83528e101bb..b739baf454de856b0568565a0dc8519bbb195fd9 100644 (file)
@@ -190,7 +190,7 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t
 "const _Vue = Vue
 const _createVNode = Vue.createVNode
 
-const _hoisted_1 = _createVNode(\\"span\\", { foo: 0 }, _toString(1), 1 /* TEXT */)
+const _hoisted_1 = _createVNode(\\"span\\", { foo: 0 }, _toString(1))
 
 return function render() {
   with (this) {
@@ -300,6 +300,20 @@ return function render() {
 }"
 `;
 
+exports[`compiler: hoistStatic transform should NOT hoist element with dynamic ref 1`] = `
+"const _Vue = Vue
+
+return function render() {
+  with (this) {
+    const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
+    
+    return (_openBlock(), _createBlock(\\"div\\", null, [
+      _createVNode(\\"div\\", { ref: foo }, null, 32 /* NEED_PATCH */)
+    ]))
+  }
+}"
+`;
+
 exports[`compiler: hoistStatic transform should NOT hoist root node 1`] = `
 "const _Vue = Vue
 
index bf4e2370b813dc28d6d3e7161e3f529be49305cf..f5567b982245104d9f72525ee156794a5d617e51 100644 (file)
@@ -273,6 +273,28 @@ describe('compiler: hoistStatic transform', () => {
     expect(generate(root).code).toMatchSnapshot()
   })
 
+  test('should NOT hoist element with dynamic ref', () => {
+    const { root, args } = transformWithHoist(`<div><div :ref="foo"/></div>`)
+    expect(root.hoists.length).toBe(0)
+    expect(args[2]).toMatchObject([
+      {
+        type: NodeTypes.ELEMENT,
+        codegenNode: {
+          callee: CREATE_VNODE,
+          arguments: [
+            `"div"`,
+            createObjectMatcher({
+              ref: `[foo]`
+            }),
+            `null`,
+            genFlagText(PatchFlags.NEED_PATCH)
+          ]
+        }
+      }
+    ])
+    expect(generate(root).code).toMatchSnapshot()
+  })
+
   test('hoist static props for elements with directives', () => {
     const { root, args } = transformWithHoist(
       `<div><div id="foo" v-foo/></div>`
@@ -521,8 +543,7 @@ describe('compiler: hoistStatic transform', () => {
                 isStatic: false,
                 isConstant: true
               }
-            },
-            '1 /* TEXT */'
+            }
           ]
         }
       ])
index ecadeddff43b5f647b6a93113ba4b6abecf99823..e528536df427b514500232f233121e63445284e7 100644 (file)
@@ -15,9 +15,8 @@ import { APPLY_DIRECTIVES } from '../runtimeHelpers'
 import { PatchFlags, isString, isSymbol } from '@vue/shared'
 import { isSlotOutlet, findProp } from '../utils'
 
-function hasDynamicKey(node: ElementNode) {
-  const keyProp = findProp(node, 'key')
-  return keyProp && keyProp.type === NodeTypes.DIRECTIVE
+function hasDynamicKeyOrRef(node: ElementNode) {
+  return findProp(node, 'key', true) || findProp(node, 'ref', true)
 }
 
 export function hoistStatic(root: RootNode, context: TransformContext) {
@@ -57,7 +56,7 @@ function walk(
       if (
         !doNotHoistNode &&
         isStaticNode(child, resultCache) &&
-        !hasDynamicKey(child)
+        !hasDynamicKeyOrRef(child)
       ) {
         // whole tree is static
         child.codegenNode = context.hoist(child.codegenNode!)
@@ -70,7 +69,7 @@ function walk(
           (!flag ||
             flag === PatchFlags.NEED_PATCH ||
             flag === PatchFlags.TEXT) &&
-          !hasDynamicKey(child)
+          !hasDynamicKeyOrRef(child)
         ) {
           let codegenNode = child.codegenNode as ElementCodegenNode
           if (codegenNode.callee === APPLY_DIRECTIVES) {
@@ -107,9 +106,9 @@ function getPatchFlag(node: PlainElementNode): number | undefined {
   return flag ? parseInt(flag, 10) : undefined
 }
 
-function isStaticNode(
+export function isStaticNode(
   node: TemplateChildNode | SimpleExpressionNode,
-  resultCache: Map<TemplateChildNode, boolean>
+  resultCache: Map<TemplateChildNode, boolean> = new Map()
 ): boolean {
   switch (node.type) {
     case NodeTypes.ELEMENT:
@@ -121,7 +120,7 @@ function isStaticNode(
         return cached
       }
       const flag = getPatchFlag(node)
-      if (!flag || flag === PatchFlags.TEXT) {
+      if (!flag) {
         // element self is static. check its children.
         for (let i = 0; i < node.children.length; i++) {
           if (!isStaticNode(node.children[i], resultCache)) {
index d54f2be39da5e4a066081decbea77af4ee86e295..571eb89d5b3db07a4614da7b17ce71c0c67a72ac 100644 (file)
@@ -28,6 +28,7 @@ import {
 } from '../runtimeHelpers'
 import { getInnerRange, isVSlot, toValidAssetId } from '../utils'
 import { buildSlots } from './vSlot'
+import { isStaticNode } from './hoistStatic'
 
 // some directive transforms (e.g. v-model) may return a symbol for runtime
 // import, which should be used instead of a resolveDirective call.
@@ -93,10 +94,11 @@ export const transformElement: NodeTransform = (node, context) => {
           } else if (node.children.length === 1) {
             const child = node.children[0]
             const type = child.type
+            // check for dynamic text children
             const hasDynamicTextChild =
               type === NodeTypes.INTERPOLATION ||
               type === NodeTypes.COMPOUND_EXPRESSION
-            if (hasDynamicTextChild) {
+            if (hasDynamicTextChild && !isStaticNode(child)) {
               patchFlag |= PatchFlags.TEXT
             }
             // pass directly if the only child is a text node
index a297d54c4fcb088fad0565564c6f81ccc47b70a4..b2f552bcb0881d9bcc75d12ee1eb7a9fb4e0171a 100644 (file)
@@ -156,15 +156,18 @@ export function findDir(
 
 export function findProp(
   node: ElementNode,
-  name: string
+  name: string,
+  dynamicOnly: boolean = false
 ): ElementNode['props'][0] | undefined {
   for (let i = 0; i < node.props.length; i++) {
     const p = node.props[i]
     if (p.type === NodeTypes.ATTRIBUTE) {
+      if (dynamicOnly) continue
       if (p.name === name && p.value && !p.value.isEmpty) {
         return p
       }
     } else if (
+      p.name === 'bind' &&
       p.arg &&
       p.arg.type === NodeTypes.SIMPLE_EXPRESSION &&
       p.arg.isStatic &&