]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: finer grained binding types for setup
authorEvan You <yyx990803@gmail.com>
Wed, 18 Nov 2020 20:17:50 +0000 (15:17 -0500)
committerEvan You <yyx990803@gmail.com>
Thu, 19 Nov 2020 00:38:38 +0000 (19:38 -0500)
packages/compiler-core/src/options.ts
packages/compiler-core/src/transforms/transformElement.ts
packages/compiler-core/src/transforms/transformExpression.ts
packages/compiler-sfc/__tests__/compileScript.spec.ts
packages/compiler-sfc/src/compileScript.ts

index bb18faf0e466db8b3f22bbf4470a44bf5fb21d6d..bb59f4940facc9a79a7e8c8a8f25a9178b07fe55 100644 (file)
@@ -62,10 +62,31 @@ export type HoistTransform = (
 ) => void
 
 export const enum BindingTypes {
+  /**
+   * returned from data()
+   */
   DATA = 'data',
+  /**
+   * decalred as a prop
+   */
   PROPS = 'props',
-  SETUP = 'setup',
-  CONST = 'const',
+  /**
+   * a let binding (may or may not be a ref)
+   */
+  SETUP_LET = 'setup-let',
+  /**
+   * a const binding that can never be a ref.
+   * these bindings don't need `unref()` calls when processed in inlined
+   * template expressions.
+   */
+  SETUP_CONST = 'setup-const',
+  /**
+   * a const binding that may be a ref.
+   */
+  SETUP_CONST_REF = 'setup-const-ref',
+  /**
+   * declared by other options, e.g. computed, inject
+   */
   OPTIONS = 'options'
 }
 
index e9e1ac5892609c17833695c3bd44e222a039f554..e3989a807eea66c44ba61858ab244fa2ea26dbf1 100644 (file)
@@ -263,20 +263,22 @@ export function resolveComponentType(
         return resolvedTag
       }
     }
-    const tagFromSetup = checkType(BindingTypes.SETUP)
-    if (tagFromSetup) {
-      return context.inline
-        ? // setup scope bindings may be refs so they need to be unrefed
-          `${context.helperString(UNREF)}(${tagFromSetup})`
-        : `$setup[${JSON.stringify(tagFromSetup)}]`
-    }
-    const tagFromConst = checkType(BindingTypes.CONST)
+    const tagFromConst = checkType(BindingTypes.SETUP_CONST)
     if (tagFromConst) {
       return context.inline
         ? // in inline mode, const setup bindings (e.g. imports) can be used as-is
           tagFromConst
         : `$setup[${JSON.stringify(tagFromConst)}]`
     }
+    const tagFromSetup =
+      checkType(BindingTypes.SETUP_LET) ||
+      checkType(BindingTypes.SETUP_CONST_REF)
+    if (tagFromSetup) {
+      return context.inline
+        ? // setup scope bindings that may be refs need to be unrefed
+          `${context.helperString(UNREF)}(${tagFromSetup})`
+        : `$setup[${JSON.stringify(tagFromSetup)}]`
+    }
   }
 
   // 4. user component (resolve)
index 0a6cc8ef00f9b982d4d53c516d7a57f43f901126..9c33cf67b6aef67e63ba098533888d7e1177f629 100644 (file)
@@ -104,9 +104,12 @@ export function processExpression(
     const type = hasOwn(bindingMetadata, raw) && bindingMetadata[raw]
     if (inline) {
       // setup inline mode
-      if (type === BindingTypes.CONST) {
+      if (type === BindingTypes.SETUP_CONST) {
         return raw
-      } else if (type === BindingTypes.SETUP) {
+      } else if (
+        type === BindingTypes.SETUP_CONST_REF ||
+        type === BindingTypes.SETUP_LET
+      ) {
         return `${context.helperString(UNREF)}(${raw})`
       } else if (type === BindingTypes.PROPS) {
         // use __props which is generated by compileScript so in ts mode
@@ -114,8 +117,12 @@ export function processExpression(
         return `__props.${raw}`
       }
     } else {
-      if (type === BindingTypes.CONST) {
-        // setup const binding in non-inline mode
+      if (
+        type === BindingTypes.SETUP_LET ||
+        type === BindingTypes.SETUP_CONST ||
+        type === BindingTypes.SETUP_CONST_REF
+      ) {
+        // setup bindings in non-inline mode
         return `$setup.${raw}`
       } else if (type) {
         return `$${type}.${raw}`
@@ -131,7 +138,9 @@ export function processExpression(
   const bailConstant = rawExp.indexOf(`(`) > -1
   if (isSimpleIdentifier(rawExp)) {
     // const bindings exposed from setup - we know they never change
-    if (bindingMetadata[node.content] === BindingTypes.CONST) {
+    // marking it as runtime constant will prevent it from being listed as
+    // a dynamic prop.
+    if (bindingMetadata[node.content] === BindingTypes.SETUP_CONST) {
       node.isRuntimeConstant = true
     }
     if (
index 41771fd242748c6858cc3f61ed0a254524eb52cb..180b24732919fb25b1183c025bfc966488dc9945 100644 (file)
@@ -1,3 +1,4 @@
+import { BindingTypes } from '@vue/compiler-dom/src'
 import { compileSFCScript as compile, assertCode } from './utils'
 
 describe('SFC compile <script setup>', () => {
@@ -33,10 +34,10 @@ const bar = 1
     assertCode(content)
     // should anayze bindings
     expect(bindings).toStrictEqual({
-      foo: 'props',
-      bar: 'const',
-      props: 'const',
-      emit: 'const'
+      foo: BindingTypes.PROPS,
+      bar: BindingTypes.SETUP_CONST,
+      props: BindingTypes.SETUP_CONST,
+      emit: BindingTypes.SETUP_CONST
     })
 
     // should remove defineOptions import and call
@@ -259,27 +260,27 @@ const { props, emit } = defineOptions({
       )
       expect(content).toMatch(`intersection: { type: Object, required: true }`)
       expect(bindings).toStrictEqual({
-        string: 'props',
-        number: 'props',
-        boolean: 'props',
-        object: 'props',
-        objectLiteral: 'props',
-        fn: 'props',
-        functionRef: 'props',
-        objectRef: 'props',
-        array: 'props',
-        arrayRef: 'props',
-        tuple: 'props',
-        set: 'props',
-        literal: 'props',
-        optional: 'props',
-        recordRef: 'props',
-        interface: 'props',
-        alias: 'props',
-        union: 'props',
-        literalUnion: 'props',
-        literalUnionMixed: 'props',
-        intersection: 'props'
+        string: BindingTypes.PROPS,
+        number: BindingTypes.PROPS,
+        boolean: BindingTypes.PROPS,
+        object: BindingTypes.PROPS,
+        objectLiteral: BindingTypes.PROPS,
+        fn: BindingTypes.PROPS,
+        functionRef: BindingTypes.PROPS,
+        objectRef: BindingTypes.PROPS,
+        array: BindingTypes.PROPS,
+        arrayRef: BindingTypes.PROPS,
+        tuple: BindingTypes.PROPS,
+        set: BindingTypes.PROPS,
+        literal: BindingTypes.PROPS,
+        optional: BindingTypes.PROPS,
+        recordRef: BindingTypes.PROPS,
+        interface: BindingTypes.PROPS,
+        alias: BindingTypes.PROPS,
+        union: BindingTypes.PROPS,
+        literalUnion: BindingTypes.PROPS,
+        literalUnionMixed: BindingTypes.PROPS,
+        intersection: BindingTypes.PROPS
       })
     })
 
@@ -380,11 +381,11 @@ const { props, emit } = defineOptions({
       expect(content).toMatch(`let d`)
       assertCode(content)
       expect(bindings).toStrictEqual({
-        foo: 'setup',
-        a: 'setup',
-        b: 'setup',
-        c: 'setup',
-        d: 'setup'
+        foo: BindingTypes.SETUP_CONST_REF,
+        a: BindingTypes.SETUP_CONST_REF,
+        b: BindingTypes.SETUP_CONST_REF,
+        c: BindingTypes.SETUP_LET,
+        d: BindingTypes.SETUP_LET
       })
     })
 
@@ -402,9 +403,9 @@ const { props, emit } = defineOptions({
       expect(content).toMatch(`return { a, b, c }`)
       assertCode(content)
       expect(bindings).toStrictEqual({
-        a: 'setup',
-        b: 'setup',
-        c: 'setup'
+        a: BindingTypes.SETUP_CONST_REF,
+        b: BindingTypes.SETUP_CONST_REF,
+        c: BindingTypes.SETUP_CONST_REF
       })
     })
 
@@ -494,12 +495,12 @@ const { props, emit } = defineOptions({
       )
       expect(content).toMatch(`return { n, a, c, d, f, g }`)
       expect(bindings).toStrictEqual({
-        n: 'setup',
-        a: 'setup',
-        c: 'setup',
-        d: 'setup',
-        f: 'setup',
-        g: 'setup'
+        n: BindingTypes.SETUP_CONST_REF,
+        a: BindingTypes.SETUP_CONST_REF,
+        c: BindingTypes.SETUP_CONST_REF,
+        d: BindingTypes.SETUP_CONST_REF,
+        f: BindingTypes.SETUP_CONST_REF,
+        g: BindingTypes.SETUP_CONST_REF
       })
       assertCode(content)
     })
@@ -518,10 +519,10 @@ const { props, emit } = defineOptions({
       expect(content).toMatch(`console.log(n.value, a.value, b.value, c.value)`)
       expect(content).toMatch(`return { n, a, b, c }`)
       expect(bindings).toStrictEqual({
-        n: 'setup',
-        a: 'setup',
-        b: 'setup',
-        c: 'setup'
+        n: BindingTypes.SETUP_CONST_REF,
+        a: BindingTypes.SETUP_CONST_REF,
+        b: BindingTypes.SETUP_CONST_REF,
+        c: BindingTypes.SETUP_CONST_REF
       })
       assertCode(content)
     })
@@ -541,9 +542,9 @@ const { props, emit } = defineOptions({
       expect(content).toMatch(`\nconst e = _ref(__e);`)
       expect(content).toMatch(`return { b, d, e }`)
       expect(bindings).toStrictEqual({
-        b: 'setup',
-        d: 'setup',
-        e: 'setup'
+        b: BindingTypes.SETUP_CONST_REF,
+        d: BindingTypes.SETUP_CONST_REF,
+        e: BindingTypes.SETUP_CONST_REF
       })
       assertCode(content)
     })
@@ -683,7 +684,10 @@ describe('SFC analyze <script> bindings', () => {
         }
       </script>
     `)
-    expect(bindings).toStrictEqual({ foo: 'props', bar: 'props' })
+    expect(bindings).toStrictEqual({
+      foo: BindingTypes.PROPS,
+      bar: BindingTypes.PROPS
+    })
   })
 
   it('recognizes props object declaration', () => {
@@ -702,10 +706,10 @@ describe('SFC analyze <script> bindings', () => {
       </script>
     `)
     expect(bindings).toStrictEqual({
-      foo: 'props',
-      bar: 'props',
-      baz: 'props',
-      qux: 'props'
+      foo: BindingTypes.PROPS,
+      bar: BindingTypes.PROPS,
+      baz: BindingTypes.PROPS,
+      qux: BindingTypes.PROPS
     })
   })
 
@@ -723,7 +727,10 @@ describe('SFC analyze <script> bindings', () => {
         }
       </script>
     `)
-    expect(bindings).toStrictEqual({ foo: 'setup', bar: 'setup' })
+    expect(bindings).toStrictEqual({
+      foo: BindingTypes.SETUP_CONST_REF,
+      bar: BindingTypes.SETUP_CONST_REF
+    })
   })
 
   it('recognizes async setup return', () => {
@@ -740,7 +747,10 @@ describe('SFC analyze <script> bindings', () => {
         }
       </script>
     `)
-    expect(bindings).toStrictEqual({ foo: 'setup', bar: 'setup' })
+    expect(bindings).toStrictEqual({
+      foo: BindingTypes.SETUP_CONST_REF,
+      bar: BindingTypes.SETUP_CONST_REF
+    })
   })
 
   it('recognizes data return', () => {
@@ -757,7 +767,10 @@ describe('SFC analyze <script> bindings', () => {
         }
       </script>
     `)
-    expect(bindings).toStrictEqual({ foo: 'data', bar: 'data' })
+    expect(bindings).toStrictEqual({
+      foo: BindingTypes.DATA,
+      bar: BindingTypes.DATA
+    })
   })
 
   it('recognizes methods', () => {
@@ -770,7 +783,7 @@ describe('SFC analyze <script> bindings', () => {
         }
       </script>
     `)
-    expect(bindings).toStrictEqual({ foo: 'options' })
+    expect(bindings).toStrictEqual({ foo: BindingTypes.OPTIONS })
   })
 
   it('recognizes computeds', () => {
@@ -787,7 +800,10 @@ describe('SFC analyze <script> bindings', () => {
         }
       </script>
     `)
-    expect(bindings).toStrictEqual({ foo: 'options', bar: 'options' })
+    expect(bindings).toStrictEqual({
+      foo: BindingTypes.OPTIONS,
+      bar: BindingTypes.OPTIONS
+    })
   })
 
   it('recognizes injections array declaration', () => {
@@ -798,7 +814,10 @@ describe('SFC analyze <script> bindings', () => {
         }
       </script>
     `)
-    expect(bindings).toStrictEqual({ foo: 'options', bar: 'options' })
+    expect(bindings).toStrictEqual({
+      foo: BindingTypes.OPTIONS,
+      bar: BindingTypes.OPTIONS
+    })
   })
 
   it('recognizes injections object declaration', () => {
@@ -812,7 +831,10 @@ describe('SFC analyze <script> bindings', () => {
         }
       </script>
     `)
-    expect(bindings).toStrictEqual({ foo: 'options', bar: 'options' })
+    expect(bindings).toStrictEqual({
+      foo: BindingTypes.OPTIONS,
+      bar: BindingTypes.OPTIONS
+    })
   })
 
   it('works for mixed bindings', () => {
@@ -843,12 +865,12 @@ describe('SFC analyze <script> bindings', () => {
       </script>
     `)
     expect(bindings).toStrictEqual({
-      foo: 'options',
-      bar: 'props',
-      baz: 'setup',
-      qux: 'data',
-      quux: 'options',
-      quuz: 'options'
+      foo: BindingTypes.OPTIONS,
+      bar: BindingTypes.PROPS,
+      baz: BindingTypes.SETUP_CONST_REF,
+      qux: BindingTypes.DATA,
+      quux: BindingTypes.OPTIONS,
+      quuz: BindingTypes.OPTIONS
     })
   })
 
@@ -864,7 +886,7 @@ describe('SFC analyze <script> bindings', () => {
       </script>
     `)
     expect(bindings).toStrictEqual({
-      foo: 'props'
+      foo: BindingTypes.PROPS
     })
   })
 })
index aa44b99ce389a0bf8bf04061d341b38dd14da72d..bdbd0b1d3e17603c303af1ab7738089638809c93 100644 (file)
@@ -159,11 +159,8 @@ export function compileScript(
       source: string
     }
   > = Object.create(null)
-  const setupBindings: Record<
-    string,
-    BindingTypes.SETUP | BindingTypes.CONST
-  > = Object.create(null)
-  const refBindings: Record<string, BindingTypes.SETUP> = Object.create(null)
+  const setupBindings: Record<string, BindingTypes> = Object.create(null)
+  const refBindings: Record<string, BindingTypes> = Object.create(null)
   const refIdentifiers: Set<Identifier> = new Set()
   const enableRefSugar = options.refSugar !== false
   let defaultExport: Node | undefined
@@ -311,7 +308,7 @@ export function compileScript(
     if (id.name[0] === '$') {
       error(`ref variable identifiers cannot start with $.`, id)
     }
-    refBindings[id.name] = setupBindings[id.name] = BindingTypes.SETUP
+    refBindings[id.name] = setupBindings[id.name] = BindingTypes.SETUP_CONST_REF
     refIdentifiers.add(id)
   }
 
@@ -787,8 +784,8 @@ export function compileScript(
   }
   for (const [key, { source }] of Object.entries(userImports)) {
     bindingMetadata[key] = source.endsWith('.vue')
-      ? BindingTypes.CONST
-      : BindingTypes.SETUP
+      ? BindingTypes.SETUP_CONST
+      : BindingTypes.SETUP_CONST_REF
   }
   for (const key in setupBindings) {
     bindingMetadata[key] = setupBindings[key]
@@ -966,8 +963,10 @@ function walkDeclaration(
           init!.type !== 'Identifier' && // const a = b
           init!.type !== 'CallExpression' && // const a = ref()
             init!.type !== 'MemberExpression') // const a = b.c
-            ? BindingTypes.CONST
-            : BindingTypes.SETUP
+            ? BindingTypes.SETUP_CONST
+            : isConst
+              ? BindingTypes.SETUP_CONST_REF
+              : BindingTypes.SETUP_LET
       } else if (id.type === 'ObjectPattern') {
         walkObjectPattern(id, bindings, isConst, isUseOptionsCall)
       } else if (id.type === 'ArrayPattern') {
@@ -980,7 +979,7 @@ function walkDeclaration(
   ) {
     // export function foo() {} / export class Foo {}
     // export declarations must be named.
-    bindings[node.id!.name] = BindingTypes.CONST
+    bindings[node.id!.name] = BindingTypes.SETUP_CONST
   }
 }
 
@@ -997,8 +996,10 @@ function walkObjectPattern(
         if (p.key === p.value) {
           // const { x } = ...
           bindings[p.key.name] = isUseOptionsCall
-            ? BindingTypes.CONST
-            : BindingTypes.SETUP
+            ? BindingTypes.SETUP_CONST
+            : isConst
+              ? BindingTypes.SETUP_CONST_REF
+              : BindingTypes.SETUP_LET
         } else {
           walkPattern(p.value, bindings, isConst, isUseOptionsCall)
         }
@@ -1007,8 +1008,8 @@ function walkObjectPattern(
       // ...rest
       // argument can only be identifer when destructuring
       bindings[(p.argument as Identifier).name] = isConst
-        ? BindingTypes.CONST
-        : BindingTypes.SETUP
+        ? BindingTypes.SETUP_CONST
+        : BindingTypes.SETUP_LET
     }
   }
 }
@@ -1032,13 +1033,15 @@ function walkPattern(
 ) {
   if (node.type === 'Identifier') {
     bindings[node.name] = isUseOptionsCall
-      ? BindingTypes.CONST
-      : BindingTypes.SETUP
+      ? BindingTypes.SETUP_CONST
+      : isConst
+        ? BindingTypes.SETUP_CONST_REF
+        : BindingTypes.SETUP_LET
   } else if (node.type === 'RestElement') {
     // argument can only be identifer when destructuring
     bindings[(node.argument as Identifier).name] = isConst
-      ? BindingTypes.CONST
-      : BindingTypes.SETUP
+      ? BindingTypes.SETUP_CONST
+      : BindingTypes.SETUP_LET
   } else if (node.type === 'ObjectPattern') {
     walkObjectPattern(node, bindings, isConst)
   } else if (node.type === 'ArrayPattern') {
@@ -1046,8 +1049,10 @@ function walkPattern(
   } else if (node.type === 'AssignmentPattern') {
     if (node.left.type === 'Identifier') {
       bindings[node.left.name] = isUseOptionsCall
-        ? BindingTypes.CONST
-        : BindingTypes.SETUP
+        ? BindingTypes.SETUP_CONST
+        : isConst
+          ? BindingTypes.SETUP_CONST_REF
+          : BindingTypes.SETUP_LET
     } else {
       walkPattern(node.left, bindings, isConst)
     }
@@ -1490,7 +1495,7 @@ function analyzeBindingsFromOptions(node: ObjectExpression): BindingMetadata {
           for (const key of getObjectExpressionKeys(bodyItem.argument)) {
             bindings[key] =
               property.key.name === 'setup'
-                ? BindingTypes.SETUP
+                ? BindingTypes.SETUP_CONST_REF
                 : BindingTypes.DATA
           }
         }