8000 revision · vuejs/rfcs@11f15b9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 11f15b9

Browse files
committed
revision
1 parent 1a7e65e commit 11f15b9

File tree

1 file changed

+67
-51
lines changed

1 file changed

+67
-51
lines changed

active-rfcs/0000-ref-sugar.md

Lines changed: 67 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Introduce a compiler-based syntax sugar for using refs without `.value`.
1111
```html
1212
<script setup>
1313
// declaring a variable that compiles to a ref
14-
let count = $ref(1)
14+
let count = $(1)
1515
1616
console.log(count) // 1
1717
@@ -61,15 +61,14 @@ This proposal aims to improve the ergonomics of refs with a set of compile-time
6161

6262
## Overview
6363

64-
- Declare reactive variables with `$ref`
64+
- Declare reactive variables with `$`
6565
- Declare reactively-derived variables with `$computed`
66-
- Destructure reactive variables from an object with `$fromRefs`
67-
- Get the raw ref object of a `$ref`-declared variable with `$raw`
66+
- Get the raw ref object of a `$`-declared variable with `$ref`
6867

69-
## `$ref`
68+
## `$`
7069

7170
```js
72-
let count = $ref(0)
71+
let count = $(0)
7372

7473
function inc() {
7574
count++
@@ -86,65 +85,86 @@ function inc() {
8685
}
8786
```
8887

89-
Variables declared with `$ref` can be accessed or mutated just like normal variables - but it enables reactivity for these operations. The `$ref` serves as a hint for the compiler to append `.value` to all references to the variable.
88+
Variables declared with `$` can be accessed or mutated just like normal variables - but it enables reactivity for these operations. `$` serves as a hint for the compiler to append `.value` to all references to the variable.
9089

91-
**Notes**
90+
Note that:
9291

93-
- `$ref` is a compile-time macro and does not need to be imported.
94-
- `$ref` can only be used with `let` because it would be pointless to declare a constant ref.
95-
- `$ref` can also be used to create a variable-like binding for other ref types, e.g. `computed`, `shallowRef` or even `customRef`:
92+
- `$` is a compile-time macro and does not need to be imported.
93+
- `$` can only be used with `let` because it would be pointless to declare a constant ref.
9694

97-
```js
98-
let count = $ref(0)
95+
## Dereferencing Existing Refs
9996

100-
const plusOne = $ref(computed(() => count + 1))
101-
console.log(plusOne)
102-
```
97+
`$` can also be used to create reactive variables from existing refs, including refs returned from `computed`, `shallowRef`, or `customRef`. This is a bit similar to the concept of "dereferencing" in languages like C++, where the value is retrived from a memory pointer (in our case, a Vue ref object).
98+
99+
```js
100+
let c = $(computed(() => count + 1))
101+
console.log(c)
102+
```
103+
104+
Compiled output:
105+
106+
```js
107+
let c = ref(computed(() => count.value + 1))
108+
console.log(c.value)
109+
```
110+
111+
This works becuase `ref()` will return its argument as-is if it is already a ref:
112+
113+
```js
114+
const foo = ref(0)
115+
const bar = ref(foo)
116+
console.log(foo === bar) // true
117+
```
103118

104119
## `$computed`
105120

106121
Because `computed` is so commonly used, it also has its dedicated macro:
107122

108123
```diff
109-
- const plusOne = $ref(computed(() => count + 1))
110-
+ const plusOne = $computed(() => count + 1)
124+
- let plusOne = $ref(computed(() => count + 1))
125+
+ let plusOne = $computed(() => count + 1)
111126
```
112127

113-
## Destructuring with `$fromRefs`
128+
## Dereferencing + Destructuring
114129

115-
It is common for a composition function to return an object of refs. To declare multiple ref bindings with destructuring, we can use the `$fromRefs` macro:
130+
It is common for a composition function to return an object of refs, and use destructuring to retrive these refs. `$` can be used in such cases as well:
116131

117132
```js
118-
const { x, y, method } = $fromRefs(useFoo())
119-
133+
let { x, y, method } = $(useFoo())
120134
console.log(x, y)
121135
method()
122136
```
123137

124138
Compiled Output:
125139

126140
```js
127-
import { shallowRef } from 'vue'
141+
import { ref } from 'vue'
128142

129-
const { x: __x, y: __y, method: __method } = useFoo()
130-
const x = shallowRef(__x)
131-
const y = shallowRef(__y)
132-
const method = shallowRef(__method)
143+
let { x: __x, y: __y, method: __method } = useFoo()
144+
const x = ref(__x)
145+
const y = ref(__y)
146+
const method = ref(__method)
133147

134148
console.log(x.value, y.value)
135149
method.value()
136150
```
137151

138-
Note this works even if a property is not a ref: for example the `method` property is a plain function here - `shallowRef` will wrap it as an actual ref so that the rest of the code could work as expected.
152+
Here if `__x` is already a ref, `ref(__x)` will simply return it as-is. Again, this works becuase `ref()` will return any existing ref as-is.
139153

140-
## Accessing Raw Refs with `$raw`
154+
If the value is not a ref (e.g. `__method` which is a function), it will be normalized into an actual ref so the rest of the code work as expected. This is because we do not have the information to determine whether a destructured property is a ref at compile time.
141155

142-
In some cases we may need to access the underlying raw ref object of a `$ref`-declared variable. We can do that with the `$raw` macro:
156+
## Accessing Raw Refs with `$ref`
157+
158+
In some cases we may need to access the underlying raw ref object of a reactive varaible. This typically happens when we have an external composable function that expects an actual ref as its argument. We can do this with the `$ref` macro:
143159

144160
```js
145-
let count = $ref(0)
161+
let count = $(0)
162+
163+
//`countRef` is the underlying ref object that syncs with `count`
164+
const countRef = $ref(count)
146165

147-
const countRef = $raw(count)
166+
countRef.value++
167+
console.log(count) // 1
148168

149169
fnThatExpectsRef(countRef)
150170
```
@@ -156,34 +176,28 @@ let count = ref(0)
156176

157177
const countRef = count
158178

179+
countRef.value++
180+
console.log(count.value) // 1
181+
159182
fnThatExpectsRef(countRef)
160183
```
161184

162-
Think of `$raw` as "do not append `.value` to anything passed to me, and return it". This means it can also be used on an object containing `$ref` variables:
163-
164-
```js
165-
let x = $ref(0)
166-
let y = $ref(0)
167-
168-
const coords = $raw({ x, y })
169-
170-
console.log(coords.x.value)
171-
```
185+
If you are familiar with languages like C++, this is also a bit similar to the "address-of" operator (`&`). However instead of the underlying memory address, we are getting a raw ref object instead.
172186

173187
## TypeScript & Tooling Integration
174188

175189
Vue will provide typings for these macros (available globally) and all types will work as expected. There are no incompatibilities with standard TypeScript semantics so the syntax would work with all existing tooling.
176190

177-
## Ref Usage in Nested Function Scopes
191+
## Usage in Nested Function Scopes
178192

179193
> This section isn't implemented as of now
180194
181-
Technically, `$ref` doesn't have to be limited to root level scope and can be used anywhere `let` declarations can be used, including nested function scope:
195+
Technically, ref sugar doesn't have to be limited to root level scope and can be used anywhere `let` declarations can be used, including nested function scope:
182196

183197
```js
184198
function useMouse() {
185-
let x = $ref(0)
186-
let y = $ref(0)
199+
let x = $(0)
200+
let y = $(0)
187201

188202
function update(e) {
189203
x = e.pageX
@@ -193,10 +207,10 @@ function useMouse() {
193207
onMounted(() => window.addEventListener('mousemove', update))
194208
onUnmounted(() => window.removeEventListener('mousemove', update))
195209

196-
return $raw({
197-
x,
198-
y
199-
})
210+
return {
211+
x: $ref(x),
212+
y: $ref(y)
213+
}
200214
}
201215
```
202216

@@ -228,12 +242,14 @@ function useMouse() {
228242

229243
## Implementation Status
230244

231-
This proposal is currently implemented in 3.2.0-beta as an experimental feature.
245+
This proposal is currently implemented in 3.2.0 as an experimental feature.
232246

233247
- It is currently only usable in `<script setup>`
234248
- It currently only processes root-level variable declarations and **does not work in nested function scopes**
235249
- It is disabled by default and must be explicitly enabled by passing the `refSugar: true` option to `@vue/compiler-sfc`. See [Appendix](#appendix) for how to enable it in specific tools.
236250

251+
**Experimental features are unstable and may change between any release types (including patch releases). By explicitly enabling an experimental feature, you are taking on the risk of potentially having to refactor into updated syntax, or even refactor away from the usage if the feature ends up being removed.**
252+
237253
# Unresolved Questions
238254

239255
## Should the macros be supported outside of SFCs?

0 commit comments

Comments
 (0)
0