feat(sfc): support $shallowRef ref sugar

This commit is contained in:
Evan You 2021-08-11 10:19:58 -04:00
parent e42d7794cb
commit 00b76d3dc1
7 changed files with 46 additions and 20 deletions

View File

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

View File

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

View File

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

View File

@ -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<T>(arg: T | Ref<T>): UnwrapRef<T>
export function $ref() {} export function $ref() {}
export function $shallowRef<T>(arg: T): T {
return arg
}
declare const ComputedRefMarker: unique symbol declare const ComputedRefMarker: unique symbol
type ComputedRefValue<T> = T & { [ComputedRefMarker]?: any } type ComputedRefValue<T> = T & { [ComputedRefMarker]?: any }

View File

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

View File

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

View File

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