Browse Source

feat(sfc): support $shallowRef ref sugar

Evan You 4 years ago
parent
commit
00b76d3dc1

+ 3 - 3
packages/compiler-sfc/__tests__/__snapshots__/compileScriptRefSugar.spec.ts.snap

@@ -33,8 +33,8 @@ return { a, b, c }
 }"
 `;
 
-exports[`<script setup> ref sugar $ref declarations 1`] = `
-"import { ref as _ref } from 'vue'
+exports[`<script setup> ref sugar $ref & $shallowRef declarations 1`] = `
+"import { ref as _ref, shallowRef as _shallowRef } from 'vue'
 
 export default {
   setup(__props, { expose }) {
@@ -42,7 +42,7 @@ export default {
 
     let foo = _ref()
     let a = _ref(1)
-    let b = _ref({
+    let b = _shallowRef({
       count: 0
     })
     let c = () => {}

+ 7 - 5
packages/compiler-sfc/__tests__/compileScriptRefSugar.spec.ts

@@ -6,24 +6,26 @@ describe('<script setup> ref sugar', () => {
     return compile(src, { refSugar: true })
   }
 
-  test('$ref declarations', () => {
+  test('$ref & $shallowRef declarations', () => {
     const { content, bindings } = compileWithRefSugar(`<script setup>
     let foo = $ref()
     let a = $ref(1)
-    let b = $ref({
+    let b = $shallowRef({
       count: 0
     })
     let c = () => {}
     let d
     </script>`)
-    expect(content).toMatch(`import { ref as _ref } from 'vue'`)
+    expect(content).toMatch(
+      `import { ref as _ref, shallowRef as _shallowRef } from 'vue'`
+    )
     expect(content).not.toMatch(`$ref()`)
     expect(content).not.toMatch(`$ref(1)`)
-    expect(content).not.toMatch(`$ref({`)
+    expect(content).not.toMatch(`$shallowRef({`)
     expect(content).toMatch(`let foo = _ref()`)
     expect(content).toMatch(`let a = _ref(1)`)
     expect(content).toMatch(`
-    let b = _ref({
+    let b = _shallowRef({
       count: 0
     })
     `)

+ 18 - 11
packages/compiler-sfc/src/compileScript.ts

@@ -65,6 +65,7 @@ const DEFINE_EXPOSE = 'defineExpose'
 const WITH_DEFAULTS = 'withDefaults'
 
 const $REF = `$ref`
+const $SHALLOW_REF = '$shallowRef'
 const $COMPUTED = `$computed`
 const $FROM_REFS = `$fromRefs`
 const $RAW = `$raw`
@@ -531,7 +532,12 @@ export function compileScript(
   }
 
   function isRefSugarCall(callee: string) {
-    return callee === $REF || callee === $COMPUTED || callee === $FROM_REFS
+    return (
+      callee === $REF ||
+      callee === $COMPUTED ||
+      callee === $FROM_REFS ||
+      callee === $SHALLOW_REF
+    )
   }
 
   function processRefSugar(
@@ -558,24 +564,28 @@ export function compileScript(
 
     const callee = (decl.init.callee as Identifier).name
     const start = decl.init.start! + startOffset
-    if (callee === $REF) {
+    if (callee === $REF || callee === $SHALLOW_REF) {
       if (statement.kind !== 'let') {
-        error(`${$REF}() bindings can only be declared with let.`, decl)
+        error(`${callee}() bindings can only be declared with let.`, decl)
       }
       if (decl.id.type !== 'Identifier') {
         error(
-          `${$REF}() bindings cannot be used with destructuring. ` +
+          `${callee}() bindings cannot be used with destructuring. ` +
             `If you are trying to destructure from an object of refs, ` +
             `use \`let { x } = $fromRefs(obj)\`.`,
           decl.id
         )
       }
       registerRefBinding(decl.id)
-      s.overwrite(start, start + $REF.length, helper('ref'))
+      s.overwrite(
+        start,
+        start + callee.length,
+        helper(callee === $REF ? 'ref' : 'shallowRef')
+      )
     } else if (callee === $COMPUTED) {
       if (decl.id.type !== 'Identifier') {
         error(
-          `${$COMPUTED}() bindings cannot be used with destructuring.`,
+          `${callee}() bindings cannot be used with destructuring.`,
           decl.id
         )
       }
@@ -584,7 +594,7 @@ export function compileScript(
     } else if (callee === $FROM_REFS) {
       if (!decl.id.type.endsWith('Pattern')) {
         error(
-          `${$FROM_REFS}() declaration must be used with destructure patterns.`,
+          `${callee}() declaration must be used with destructure patterns.`,
           decl
         )
       }
@@ -1124,10 +1134,7 @@ export function compileScript(
         return false // skip walk
       } else if (
         parent &&
-        isCallOf(
-          node,
-          id => id === $REF || id === $FROM_REFS || id === $COMPUTED
-        ) &&
+        isCallOf(node, isRefSugarCall) &&
         (parent.type !== 'VariableDeclarator' || node !== parent.init)
       ) {
         error(

+ 4 - 0
packages/runtime-core/src/helpers/refSugar.ts

@@ -3,6 +3,10 @@ import { Ref, UnwrapRef, ShallowUnwrapRef, ComputedRef } from '@vue/reactivity'
 export function $ref<T>(arg: T | Ref<T>): UnwrapRef<T>
 export function $ref() {}
 
+export function $shallowRef<T>(arg: T): T {
+  return arg
+}
+
 declare const ComputedRefMarker: unique symbol
 type ComputedRefValue<T> = T & { [ComputedRefMarker]?: any }
 

+ 7 - 1
packages/runtime-core/src/index.ts

@@ -354,4 +354,10 @@ export const compatUtils = (
 
 // Ref sugar macros ------------------------------------------------------------
 // for dts generation only
-export { $ref, $computed, $raw, $fromRefs } from './helpers/refSugar'
+export {
+  $ref,
+  $shallowRef,
+  $computed,
+  $raw,
+  $fromRefs
+} from './helpers/refSugar'

+ 2 - 0
packages/runtime-core/types/scriptSetupHelpers.d.ts

@@ -6,6 +6,7 @@ type _defineExpose = typeof defineExpose
 type _withDefaults = typeof withDefaults
 
 type _ref = typeof $ref
+type _shallowRef = typeof $shallowRef
 type _computed = typeof $computed
 type _fromRefs = typeof $fromRefs
 type _raw = typeof $raw
@@ -17,6 +18,7 @@ declare global {
   const withDefaults: _withDefaults
 
   const $ref: _ref
+  const $shallowRef: _shallowRef
   const $computed: _computed
   const $fromRefs: _fromRefs
   const $raw: _raw

+ 5 - 0
test-dts/refSugar.test-d.ts

@@ -1,6 +1,7 @@
 import {
   expectType,
   $ref,
+  $shallowRef,
   $computed,
   $fromRefs,
   $raw,
@@ -14,6 +15,10 @@ expectType<number>($ref(1))
 expectType<number>($ref(ref(1)))
 expectType<{ foo: number }>($ref({ foo: ref(1) }))
 
+// $shallowRef
+expectType<number>($shallowRef(1))
+expectType<{ foo: Ref<number> }>($shallowRef({ foo: ref(1) }))
+
 // $computed
 expectType<number>($computed(() => 1))
 let b = $ref(1)