]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(compiler-core): follow up on #276
authorEvan You <yyx990803@gmail.com>
Tue, 15 Oct 2019 15:51:52 +0000 (11:51 -0400)
committerEvan You <yyx990803@gmail.com>
Tue, 15 Oct 2019 15:51:52 +0000 (11:51 -0400)
packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap
packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts
packages/compiler-core/src/parse.ts
packages/compiler-core/src/transforms/transformExpression.ts

index dba4bf1f095287845076cdb6c52fee6e16efdc7e..9ad4a155c2d146d2d195b4bc2638e83528e101bb 100644 (file)
@@ -173,7 +173,7 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t
 "const _Vue = Vue
 const _createVNode = Vue.createVNode
 
-const _hoisted_1 = _createVNode(\\"span\\", null, [\\"foo \\", _toString(1), _toString(2)])
+const _hoisted_1 = _createVNode(\\"span\\", null, [\\"foo \\", _toString(1), _toString(true)])
 
 return function render() {
   with (this) {
@@ -203,7 +203,7 @@ return function render() {
 }"
 `;
 
-exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that with scope variable (2) 1`] = `
+exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that refer scope variables (2) 1`] = `
 "const _Vue = Vue
 
 return function render() {
@@ -221,7 +221,24 @@ return function render() {
 }"
 `;
 
-exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that with scope variable 1`] = `
+exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that refer scope variables (v-slot) 1`] = `
+"const _Vue = Vue
+
+return function render() {
+  with (this) {
+    const { toString: _toString, resolveComponent: _resolveComponent, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
+    
+    const _component_Comp = _resolveComponent(\\"Comp\\")
+    
+    return (_openBlock(), _createBlock(_component_Comp, null, {
+      default: ({ foo }) => [_toString(_ctx.foo)],
+      _compiled: true
+    }))
+  }
+}"
+`;
+
+exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that refer scope variables 1`] = `
 "const _Vue = Vue
 
 return function render() {
index d998aa26cf69203e527d209c9f64c2dd08871d42..bf4e2370b813dc28d6d3e7161e3f529be49305cf 100644 (file)
@@ -446,7 +446,7 @@ describe('compiler: hoistStatic transform', () => {
   describe('prefixIdentifiers', () => {
     test('hoist nested static tree with static interpolation', () => {
       const { root, args } = transformWithHoist(
-        `<div><span>foo {{ 1 }} {{ 2 }}</span></div>`,
+        `<div><span>foo {{ 1 }} {{ true }}</span></div>`,
         {
           prefixIdentifiers: true
         }
@@ -474,7 +474,7 @@ describe('compiler: hoistStatic transform', () => {
               {
                 type: NodeTypes.INTERPOLATION,
                 content: {
-                  content: `2`,
+                  content: `true`,
                   isStatic: false,
                   isConstant: true
                 }
@@ -600,7 +600,7 @@ describe('compiler: hoistStatic transform', () => {
       expect(generate(root).code).toMatchSnapshot()
     })
 
-    test('should NOT hoist expressions that with scope variable', () => {
+    test('should NOT hoist expressions that refer scope variables', () => {
       const { root } = transformWithHoist(
         `<div><p v-for="o in list"><span>{{ o }}</span></p></div>`,
         {
@@ -612,7 +612,7 @@ describe('compiler: hoistStatic transform', () => {
       expect(generate(root).code).toMatchSnapshot()
     })
 
-    test('should NOT hoist expressions that with scope variable (2)', () => {
+    test('should NOT hoist expressions that refer scope variables (2)', () => {
       const { root } = transformWithHoist(
         `<div><p v-for="o in list"><span>{{ o + 'foo' }}</span></p></div>`,
         {
@@ -623,5 +623,17 @@ describe('compiler: hoistStatic transform', () => {
       expect(root.hoists.length).toBe(0)
       expect(generate(root).code).toMatchSnapshot()
     })
+
+    test('should NOT hoist expressions that refer scope variables (v-slot)', () => {
+      const { root } = transformWithHoist(
+        `<Comp v-slot="{ foo }">{{ foo }}</Comp>`,
+        {
+          prefixIdentifiers: true
+        }
+      )
+
+      expect(root.hoists.length).toBe(0)
+      expect(generate(root).code).toMatchSnapshot()
+    })
   })
 })
index c369d45644d29e2d617ea3c500ceacb11b4e480e..f2520d179166d43a1a18b71d0608ab7b5656d2b2 100644 (file)
@@ -564,12 +564,9 @@ function parseAttribute(
       )
       let content = match[2]
       let isStatic = true
-      // Non-dynamic arg is a constant.
-      let isConstant = true
 
       if (content.startsWith('[')) {
         isStatic = false
-        isConstant = false
 
         if (!content.endsWith(']')) {
           emitError(
@@ -585,7 +582,7 @@ function parseAttribute(
         type: NodeTypes.SIMPLE_EXPRESSION,
         content,
         isStatic,
-        isConstant,
+        isConstant: isStatic,
         loc
       }
     }
@@ -611,7 +608,8 @@ function parseAttribute(
         type: NodeTypes.SIMPLE_EXPRESSION,
         content: value.content,
         isStatic: false,
-        // Set `isConstant` to false by default and will decide in transformExpression
+        // Treat as non-constant by default. This can be potentially set to
+        // true by `transformExpression` to make it eligible for hoisting.
         isConstant: false,
         loc: value.loc
       },
index 083b3023cc7725e5ee9543ca2834821ac5ceee05..2bac56a8423f34979120847c1d29c1e3fff6b4f6 100644 (file)
@@ -91,6 +91,9 @@ export function processExpression(
       !literalsWhitelist.has(rawExp)
     ) {
       node.content = `_ctx.${rawExp}`
+    } else if (!context.identifiers[rawExp]) {
+      // mark node constant for hoisting unless it's referring a scope variable
+      node.isConstant = true
     }
     return node
   }
@@ -109,13 +112,13 @@ export function processExpression(
   const ids: (Identifier & PrefixMeta)[] = []
   const knownIds = Object.create(context.identifiers)
 
-  let isConstant = true
   // walk the AST and look for identifiers that need to be prefixed with `_ctx.`.
   walkJS(ast, {
     enter(node: Node & PrefixMeta, parent) {
       if (node.type === 'Identifier') {
         if (!ids.includes(node)) {
-          if (!knownIds[node.name] && shouldPrefix(node, parent)) {
+          const needPrefix = shouldPrefix(node, parent)
+          if (!knownIds[node.name] && needPrefix) {
             if (isPropertyShorthand(node, parent)) {
               // property shorthand like { foo }, we need to add the key since we
               // rewrite the value
@@ -123,14 +126,11 @@ export function processExpression(
             }
             node.name = `_ctx.${node.name}`
             node.isConstant = false
-            isConstant = false
             ids.push(node)
           } else if (!isStaticPropertyKey(node, parent)) {
-            // This means this identifier is pointing to a scope variable (a v-for alias, or a v-slot prop)
-            // which is also dynamic and cannot be hoisted.
-            node.isConstant = !(
-              knownIds[node.name] && shouldPrefix(node, parent)
-            )
+            // The identifier is considered constant unless it's pointing to a
+            // scope variable (a v-for alias, or a v-slot prop)
+            node.isConstant = !(needPrefix && knownIds[node.name])
             // also generate sub-expressions for other identifiers for better
             // source map support. (except for property keys which are static)
             ids.push(node)
@@ -220,7 +220,7 @@ export function processExpression(
     ret = createCompoundExpression(children, node.loc)
   } else {
     ret = node
-    ret.isConstant = isConstant
+    ret.isConstant = true
   }
   ret.identifiers = Object.keys(knownIds)
   return ret