Skip to content

Commit 9d759d9

Browse files
authored
3.2 updates (#1157)
1 parent 7b504b0 commit 9d759d9

15 files changed

+1328
-200
lines changed

src/.vuepress/config.js

+43-8
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const sidebar = {
8080
title: 'Advanced Guides',
8181
collapsable: false,
8282
children: [
83+
'/guide/web-components',
8384
{
8485
title: 'Reactivity',
8586
children: [
@@ -106,7 +107,12 @@ const sidebar = {
106107
{
107108
title: 'Scaling Up',
108109
collapsable: false,
109-
children: ['/guide/routing', '/guide/state-management', '/guide/ssr', '/guide/security']
110+
children: [
111+
'/guide/routing',
112+
'/guide/state-management',
113+
'/guide/ssr',
114+
'/guide/security'
115+
]
110116
},
111117
{
112118
title: 'Accessibility',
@@ -148,10 +154,33 @@ const sidebar = {
148154
children: [
149155
'/api/basic-reactivity',
150156
'/api/refs-api',
151-
'/api/computed-watch-api'
157+
'/api/computed-watch-api',
158+
'/api/effect-scope',
152159
]
153160
},
154-
'/api/composition-api'
161+
'/api/composition-api',
162+
{
163+
title: 'Single File Components',
164+
collapsable: false,
165+
children: [
166+
{
167+
title: 'Spec',
168+
path: '/api/sfc-spec'
169+
},
170+
{
171+
title: 'Tooling',
172+
path: '/api/sfc-tooling'
173+
},
174+
{
175+
title: '<script setup>',
176+
path: '/api/sfc-script-setup'
177+
},
178+
{
179+
title: '<style> Features',
180+
path: '/api/sfc-style'
181+
}
182+
]
183+
}
155184
],
156185
examples: [
157186
{
@@ -379,8 +408,7 @@ module.exports = {
379408
},
380409
{
381410
text: 'Vue Test Utils',
382-
link:
383-
'https://next.vue-test-utils.vuejs.org/guide/'
411+
link: 'https://next.vue-test-utils.vuejs.org/guide/'
384412
},
385413
{
386414
text: 'Devtools',
@@ -491,11 +519,18 @@ module.exports = {
491519
const date = new Date(timestamp)
492520

493521
const digits = [
494-
date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate(),
495-
date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds()
522+
date.getUTCFullYear(),
523+
date.getUTCMonth() + 1,
524+
date.getUTCDate(),
525+
date.getUTCHours(),
526+
date.getUTCMinutes(),
527+
date.getUTCSeconds()
496528
].map(num => String(num).padStart(2, '0'))
497529

498-
return '{0}-{1}-{2}, {3}:{4}:{5} UTC'.replace(/{(\d)}/g, (_, num) => digits[num])
530+
return '{0}-{1}-{2}, {3}:{4}:{5} UTC'.replace(
531+
/{(\d)}/g,
532+
(_, num) => digits[num]
533+
)
499534
}
500535
}
501536
],

src/api/computed-watch-api.md

+32-3
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,31 @@ console.log(count.value) // 0
3434

3535
```ts
3636
// read-only
37-
function computed<T>(getter: () => T): Readonly<Ref<Readonly<T>>>
37+
function computed<T>(
38+
getter: () => T,
39+
debuggerOptions?: DebuggerOptions
40+
): Readonly<Ref<Readonly<T>>>
3841

3942
// writable
40-
function computed<T>(options: { get: () => T; set: (value: T) => void }): Ref<T>
43+
function computed<T>(
44+
options: {
45+
get: () => T
46+
set: (value: T) => void
47+
},
48+
debuggerOptions?: DebuggerOptions
49+
): Ref<T>
50+
51+
interface DebuggerOptions {
52+
onTrack?: (event: DebuggerEvent) => void
53+
onTrigger?: (event: DebuggerEvent) => void
54+
}
55+
56+
interface DebuggerEvent {
57+
effect: ReactiveEffect
58+
target: any
59+
type: OperationTypes
60+
key: string | symbol | undefined
61+
}
4162
```
4263

4364
## `watchEffect`
@@ -84,9 +105,17 @@ type StopHandle = () => void
84105
85106
**See also**: [`watchEffect` guide](../guide/reactivity-computed-watchers.html#watcheffect)
86107
108+
## `watchPostEffect` <Badge text="3.2+" />
109+
110+
Alias of `watchEffect` with `flush: 'post'` option.
111+
112+
## `watchSyncEffect` <Badge text="3.2+" />
113+
114+
Alias of `watchEffect` with `flush: 'sync'` option.
115+
87116
## `watch`
88117
89-
The `watch` API is the exact equivalent of the Options API [this.$watch](./instance-methods.html#watch) (and the corresponding [watch](./options-data.html#watch) option). `watch` requires watching a specific data source and applies side effects in a separate callback function. It also is lazy by default - i.e. the callback is only called when the watched source has changed.
118+
The `watch` API is the exact equivalent of the Options API [this.\$watch](./instance-methods.html#watch) (and the corresponding [watch](./options-data.html#watch) option). `watch` requires watching a specific data source and applies side effects in a separate callback function. It also is lazy by default - i.e. the callback is only called when the watched source has changed.
90119
91120
- Compared to [watchEffect](#watcheffect), `watch` allows us to:
92121

src/api/directives.md

+70-14
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@
237237

238238
## v-bind
239239

240-
- **Shorthand:** `:`
240+
- **Shorthand:** `:` or `.` (when using `.prop` modifier)
241241

242242
- **Expects:** `any (with argument) | Object (without argument)`
243243

@@ -246,6 +246,8 @@
246246
- **Modifiers:**
247247

248248
- `.camel` - transform the kebab-case attribute name into camelCase.
249+
- `.prop` - force a binding to be set as a DOM property. <Badge text="3.2+"/>
250+
- `.attr` - force a binding to be set as a DOM attribute. <Badge text="3.2+"/>
249251

250252
- **Usage:**
251253

@@ -278,23 +280,34 @@
278280
<!-- class binding -->
279281
<div :class="{ red: isRed }"></div>
280282
<div :class="[classA, classB]"></div>
281-
<div :class="[classA, { classB: isB, classC: isC }]">
282-
<!-- style binding -->
283-
<div :style="{ fontSize: size + 'px' }"></div>
284-
<div :style="[styleObjectA, styleObjectB]"></div>
283+
<div :class="[classA, { classB: isB, classC: isC }]"></div>
285284

286-
<!-- binding an object of attributes -->
287-
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
285+
<!-- style binding -->
286+
<div :style="{ fontSize: size + 'px' }"></div>
287+
<div :style="[styleObjectA, styleObjectB]"></div>
288288

289-
<!-- prop binding. "prop" must be declared in my-component. -->
290-
<my-component :prop="someThing"></my-component>
289+
<!-- binding an object of attributes -->
290+
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
291291

292-
<!-- pass down parent props in common with a child component -->
293-
<child-component v-bind="$props"></child-component>
292+
<!-- prop binding. "prop" must be declared in my-component. -->
293+
<my-component :prop="someThing"></my-component>
294294

295-
<!-- XLink -->
296-
<svg><a :xlink:special="foo"></a></svg>
297-
</div>
295+
<!-- pass down parent props in common with a child component -->
296+
<child-component v-bind="$props"></child-component>
297+
298+
<!-- XLink -->
299+
<svg><a :xlink:special="foo"></a></svg>
300+
```
301+
302+
When setting a binding on an element, Vue by default checks whether the element has the key defined as a property using an `in` operator check. If the property is defined, Vue will set the value as a DOM property instead of an attribute. This should work in most cases, but you can override this behavior by explicitly using `.prop` or `.attr` modifiers. This is sometimes necessary, especially when [working with custom elements](/guide/web-components.html#passing-dom-properties).
303+
304+
The `.prop` modifier also has a dedicated shorthand, `.`:
305+
306+
```html
307+
<div :someProperty.prop="someObject"></div>
308+
309+
<!-- equivalent to -->
310+
<div .someProperty="someObject"></div>
298311
```
299312

300313
The `.camel` modifier allows camelizing a `v-bind` attribute name when using in-DOM templates, e.g. the SVG `viewBox` attribute:
@@ -451,8 +464,51 @@
451464
</ul>
452465
```
453466

467+
Since 3.2, you can also memoize part of the template with invalidation conditions using [`v-memo`](#v-memo).
468+
454469
- **See also:**
455470
- [Data Binding Syntax - interpolations](../guide/template-syntax.html#text)
471+
- [v-memo](#v-memo)
472+
473+
## v-memo <Badge text="3.2+" />
474+
475+
- **Expects:** `Array`
476+
477+
- **Details:**
478+
479+
Memoize a sub-tree of the template. Can be used on both elements and components. The directive expects a fixed-length array of dependency values to compare for the memoization. If every value in the array was the same as last render, then updates for the entire sub-tree will be skipped. For example:
480+
481+
```html
482+
<div v-memo="[valueA, valueB]">
483+
...
484+
</div>
485+
```
486+
487+
When the component re-renders, if both `valueA` and `valueB` remain the same, all updates for this `<div>` and its children will be skipped. In fact, even the Virtual DOM VNode creation will also be skipped since the memoized copy of the sub-tree can be reused.
488+
489+
It is important to specify the memoization array correctly, otherwise we may skip updates that should indeed be applied. `v-memo` with an empty dependency array (`v-memo="[]"`) would be functionally equivalent to `v-once`.
490+
491+
**Usage with `v-for`**
492+
493+
`v-memo` is provided solely for micro optimizations in performance-critical scenarios and should be rarely needed. The most common case where this may prove helpful is when rendering large `v-for` lists (where `length > 1000`):
494+
495+
```html
496+
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
497+
<p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
498+
<p>...more child nodes</p>
499+
</div>
500+
```
501+
502+
When the component's `selected` state changes, a large amount of VNodes will be created even though most of the items remained exactly the same. The `v-memo` usage here is essentially saying "only update this item if it went from non-selected to selected, or the other way around". This allows every unaffected item to reuse its previous VNode and skip diffing entirely. Note we don't need to include `item.id` in the memo dependency array here since Vue automatically infers it from the item's `:key`.
503+
504+
:::warning
505+
When using `v-memo` with `v-for`, make sure they are used on the same element. **`v-memo` does not work inside `v-for`.**
506+
:::
507+
508+
`v-memo` can also be used on components to manually prevent unwanted updates in certain edge cases where the child component update check has been de-optimized. But again, it is the developer's responsibility to specify correct dependency arrays to avoid skipping necessary updates.
509+
510+
- **See also:**
511+
- [v-once](#v-once)
456512

457513
## v-is <Badge text="deprecated" type="warning" />
458514

src/api/effect-scope.md

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Effect Scope API <Badge text="3.2+" />
2+
3+
:::info
4+
Effect scope is an advanced API primarily intended for library authors. For details on how to leverage this API, please consult its corresponding [RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0041-reactivity-effect-scope.md).
5+
:::
6+
7+
## `effectScope`
8+
9+
Creates an effect scope object which can capture the reactive effects (e.g. computed and watchers) created within it so that these effects can be disposed together.
10+
11+
**Typing:**
12+
13+
```ts
14+
function effectScope(detached?: boolean): EffectScope
15+
16+
interface EffectScope {
17+
run<T>(fn: () => T): T | undefined // undefined if scope is inactive
18+
stop(): void
19+
}
20+
```
21+
22+
**Example:**
23+
24+
```js
25+
const scope = effectScope()
26+
27+
scope.run(() => {
28+
const doubled = computed(() => counter.value * 2)
29+
30+
watch(doubled, () => console.log(doubled.value))
31+
32+
watchEffect(() => console.log('Count: ', doubled.value))
33+
})
34+
35+
// to dispose all effects in the scope
36+
scope.stop()
37+
```
38+
39+
## `getCurrentScope`
40+
41+
Returns the current active [effect scope](#effectscope) if there is one.
42+
43+
**Typing:**
44+
45+
```ts
46+
function getCurrentScope(): EffectScope | undefined
47+
```
48+
49+
## `onScopeDispose`
50+
51+
Registers a dispose callback on the current active [effect scope](#effectscope). The callback will be invoked when the associated effect scope is stopped.
52+
53+
This method can be used as a non-component-coupled replacement of `onUnmounted` in reusable composition functions, since each Vue component's `setup()` function is also invoked in an effect scope.
54+
55+
**Typing:**
56+
57+
```ts
58+
function onScopeDispose(fn: () => void): void
59+
```

0 commit comments

Comments
 (0)