You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: active-rfcs/0000-ref-sugar.md
+67-51Lines changed: 67 additions & 51 deletions
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,7 @@ Introduce a compiler-based syntax sugar for using refs without `.value`.
11
11
```html
12
12
<scriptsetup>
13
13
// declaring a variable that compiles to a ref
14
-
let count =$ref(1)
14
+
let count =$(1)
15
15
16
16
console.log(count) // 1
17
17
@@ -61,15 +61,14 @@ This proposal aims to improve the ergonomics of refs with a set of compile-time
61
61
62
62
## Overview
63
63
64
-
- Declare reactive variables with `$ref`
64
+
- Declare reactive variables with `$`
65
65
- 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`
68
67
69
-
## `$ref`
68
+
## `$`
70
69
71
70
```js
72
-
let count =$ref(0)
71
+
let count =$(0)
73
72
74
73
functioninc() {
75
74
count++
@@ -86,65 +85,86 @@ function inc() {
86
85
}
87
86
```
88
87
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.
90
89
91
-
**Notes**
90
+
Note that:
92
91
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.
96
94
97
-
```js
98
-
let count =$ref(0)
95
+
## Dereferencing Existing Refs
99
96
100
-
constplusOne=$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
+
constfoo=ref(0)
115
+
constbar=ref(foo)
116
+
console.log(foo === bar) // true
117
+
```
103
118
104
119
## `$computed`
105
120
106
121
Because `computed` is so commonly used, it also has its dedicated macro:
107
122
108
123
```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)
111
126
```
112
127
113
-
## Destructuring with `$fromRefs`
128
+
## Dereferencing + Destructuring
114
129
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:
116
131
117
132
```js
118
-
const { x, y, method } =$fromRefs(useFoo())
119
-
133
+
let { x, y, method } =$(useFoo())
120
134
console.log(x, y)
121
135
method()
122
136
```
123
137
124
138
Compiled Output:
125
139
126
140
```js
127
-
import { shallowRef } from'vue'
141
+
import { ref } from'vue'
128
142
129
-
const { x:__x, y:__y, method:__method } =useFoo()
130
-
constx=shallowRef(__x)
131
-
consty=shallowRef(__y)
132
-
constmethod=shallowRef(__method)
143
+
let { x: __x, y: __y, method: __method } =useFoo()
144
+
constx=ref(__x)
145
+
consty=ref(__y)
146
+
constmethod=ref(__method)
133
147
134
148
console.log(x.value, y.value)
135
149
method.value()
136
150
```
137
151
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.
139
153
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.
141
155
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:
143
159
144
160
```js
145
-
let count =$ref(0)
161
+
let count =$(0)
162
+
163
+
//`countRef` is the underlying ref object that syncs with `count`
164
+
constcountRef=$ref(count)
146
165
147
-
constcountRef=$raw(count)
F438
code>
166
+
countRef.value++
167
+
console.log(count) // 1
148
168
149
169
fnThatExpectsRef(countRef)
150
170
```
@@ -156,34 +176,28 @@ let count = ref(0)
156
176
157
177
constcountRef= count
158
178
179
+
countRef.value++
180
+
console.log(count.value) // 1
181
+
159
182
fnThatExpectsRef(countRef)
160
183
```
161
184
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
-
constcoords=$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.
172
186
173
187
## TypeScript & Tooling Integration
174
188
175
189
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.
176
190
177
-
## Ref Usage in Nested Function Scopes
191
+
## Usage in Nested Function Scopes
178
192
179
193
> This section isn't implemented as of now
180
194
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:
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.
232
246
233
247
- It is currently only usable in `<script setup>`
234
248
- It currently only processes root-level variable declarations and **does not work in nested function scopes**
235
249
- 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.
236
250
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
+
237
253
# Unresolved Questions
238
254
239
255
## Should the macros be supported outside of SFCs?
0 commit comments