Skip to content

Commit 4b554c5

Browse files
committed
3.2 updates (#1157)
1 parent 1c16c08 commit 4b554c5

15 files changed

+1286
-193
lines changed

src/.vitepress/config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ module.exports = {
287287
},
288288
{
289289
text: 'Vue and Web Components',
290-
link: '/guide/vue-and-web-components'
290+
link: '/guide/web-components'
291291
},
292292
{
293293
text: 'Vue for React Devs',

src/api/computed-watch-api.md

Lines changed: 32 additions & 3 deletions
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

Lines changed: 70 additions & 14 deletions
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

Lines changed: 59 additions & 0 deletions
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+
```

src/api/global-api.md

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,44 @@ const AsyncComp = defineAsyncComponent({
236236

237237
**See also**: [Dynamic and Async components](../guide/component-dynamic-async.html)
238238

239+
## defineCustomElement <Badge text="3.2+" />
240+
241+
This method accepts the same argument as [`defineComponent`](#definecomponent), but instead returns a native [Custom Element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) that can be used within any framework, or with no frameworks at all.
242+
243+
Usage example:
244+
245+
```html
246+
<my-vue-element></my-vue-element>
247+
```
248+
249+
```js
250+
import { defineCustomElement } from 'vue'
251+
252+
const MyVueElement = defineCustomElement({
253+
// normal Vue component options here
254+
props: {},
255+
emits: {},
256+
template: `...`,
257+
258+
// defineCustomElement only: CSS to be injected into shadow root
259+
styles: [`/* inlined css */`]
260+
})
261+
262+
// Register the custom element.
263+
// After registration, all `<my-vue-element>` tags on the page will be upgraded.
264+
customElements.define('my-vue-element', MyVueElement)
265+
266+
// You can also programmatically instantiate the element:
267+
// (can only be done after registration)
268+
document.body.appendChild(
269+
new MyVueElement({
270+
// initial props (optional)
271+
})
272+
)
273+
```
274+
275+
For more details on building Web Components with Vue, especially with Single File Components, see [Vue and Web Components](/guide/web-components.html#building-custom-elements-with-vue).
276+
239277
## resolveComponent
240278

241279
:::warning
@@ -481,10 +519,13 @@ export default {
481519
inheritAttrs: false,
482520

483521
render() {
484-
const props = mergeProps({
485-
// The class will be merged with any class from $attrs
486-
class: 'active'
487-
}, this.$attrs)
522+
const props = mergeProps(
523+
{
524+
// The class will be merged with any class from $attrs
525+
class: 'active'
526+
},
527+
this.$attrs
528+
)
488529

489530
return h('div', props)
490531
}
@@ -504,12 +545,17 @@ Allows CSS modules to be accessed within the [`setup`](/api/composition-api.html
504545
import { h, useCssModule } from 'vue'
505546
506547
export default {
507-
setup () {
548+
setup() {
508549
const style = useCssModule()
509550
510-
return () => h('div', {
511-
class: style.success
512-
}, 'Task complete!')
551+
return () =>
552+
h(
553+
'div',
554+
{
555+
class: style.success
556+
},
557+
'Task complete!'
558+
)
513559
}
514560
}
515561
</script>
@@ -521,7 +567,7 @@ export default {
521567
</style>
522568
```
523569

524-
For more information about using CSS modules, see [Vue Loader - CSS Modules](https://vue-loader.vuejs.org/guide/css-modules.html).
570+
For more information about using CSS modules, see [SFC Style Features: `<style module>`](/api/sfc-style.html#style-module).
525571

526572
### Arguments
527573

src/api/reactivity-api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ The Reactivity API contains the following sections:
55
- [Basic Reactivity APIs](/api/basic-reactivity.html)
66
- [Refs](/api/refs-api.html)
77
- [Computed and watch](/api/computed-watch-api.html)
8+
- [Effect Scope API](/api/effect-scope.html)

0 commit comments

Comments
 (0)