Vue3 Preliminary Exploration (4) Template Command

Continue to take a look at the migration doc of Vue3

v-model

2.x

In 2.x, using v-model on a component is equivalent to binding the value prop and input events:

1
2
3
4
5
<ChildComponent v-model="pageTitle" />

<! -- Abbreviation: -- >

<ChildComponent :value="pageTitle" @input="pageTitle = $event" />

If you want to change the property or event name to something else, you need to add the model option to the ChildComponent component:

  • Modify the prop property bound by v-model through model.prop configuration in the subcomponent. For example, in the following example, the prop bound by the v-model property changes from value to title.
  • In the child component, through model.event configuration, you can modify the event that needs to be passed to the parent component when the value bound by v-model changes in the child component. For example, in the following example, the triggered event is changed from the default input to change.
  • Since the v-model binding prop is changed to title by the model.prop configuration, value can be passed in as a normal prop.
1
2
3
<!-- ParentComponent.vue -->

<ChildComponent v-model="pageTitle" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ChildComponent.vue

export default {
model: {
prop: 'title',
event: 'change'
},
props: {
//This will allow the'value 'attribute to be used for other purposes
value: String,
//use'title 'instead of'value' as the prop of the model
title: {
type: String,
default: 'Default title'
}
}
}

Therefore, in this example, the abbreviation of’v-model ’ (the abbreviation here means that the special configuration above in the subcomponent can be omitted by writing the following) is as follows:

1
<ChildComponent :title="pageTitle" @change="pageTitle = $event" />

Use

In some cases, we may need to “bind” a certain prop in both directions (except for the case where the prop was bound with’v-model ‘earlier). To do this, we recommend using’update: myPropName’ to throw the event. For example, for’ChildComponent ‘with’title’ prop in the previous example, we can convey the intention to assign a new value to the parent in the following way:

1
this.$emit('update:title', newValue)

If necessary, the parent can listen for the event and update local data properties. For example:

1
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

For convenience, we can use the .sync modifier to abbreviate as follows:

1
<ChildComponent :title.sync="pageTitle" />

3.x

In 3.x, the v-model on the custom component is equivalent to passing the modelValue prop and receiving the update: modelValue event thrown:

1
2
3
4
5
6
7
8
<ChildComponent v-model="pageTitle" />

<! -- Abbreviation: -- >

<ChildComponent
:modelValue="pageTitle"
@update:modelValue="pageTitle = $event"
/>

v-model

If we need to change the name of’model ‘instead of changing the’model’ option in the component, we can now pass an * argument * to’model ':

1
2
3
4
5
<ChildComponent v-model:title="pageTitle" />

<! -- Abbreviation: -- >

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

This can also be used as an alternative to the .sync modifier and allows us to use multiple v-models on custom components.

1
2
3
4
5
6
7
8
9
10
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />

<! -- Abbreviation: -- >

<ChildComponent
:title="pageTitle"
@update:title="pageTitle = $event"
:content="pageContent"
@update:content="pageContent = $event"
/>

v-model

In addition to 2.x hardcoding v-model modifiers like .trim, 3.x now supports custom modifiers:

1
<ChildComponent v-model.capitalize="pageTitle" />

See another blog post about custom events in https://sunra.top/posts/4258a220/

Key

  • Add each branch item of v-if, v-else, v-else-if, key will no longer be necessary, because now Vue will automatically generate a unique key
    • ** Non-compliant **: If you manually supply’key ‘, then each branch must use a unique’key’. You cannot force reuse of branches by intentionally using the same’key '.
      Non-compliant: The key of < template v-for > should be set on the < template > tag (not on its sub-node).

In the condition branch

Vue 2.x recommends using’key ‘in the branch of’v-if’/‘v-else’/‘v-else-if’.

1
2
3
<!-- Vue 2.x -->
<div v-if="condition" key="yes">Yes</div>
<div v-else key="no">No</div>

This example still works fine in Vue 3.x. However, we no longer recommend continuing to use the’key ‘attribute in the branch of’v-if’/‘v-else’/‘v-else-if’, because when no’key ‘is provided for the conditional branch, a unique’key’ will also be generated automatically.

1
2
3
<!-- Vue 3.x -->
<div v-if="condition">Yes</div>
<div v-else>No</div>

The non-compliant change is reflected in the fact that if you manually provide the’key ‘, then each branch must use a unique’key’. Therefore, in most cases, it is not necessary to set these’keys’.

1
2
3
4
5
6
7
8
9
10
11
<!-- Vue 2.x -->
<div v-if="condition" key="a">Yes</div>
<div v-else key="a">No</div>

<!-- Vue 3.x (recommended solution: remove keys) -->
<div v-if="condition">Yes</div>
<div v-else>No</div>

<!-- Vue 3.x (alternate solution: make sure the keys are always unique) -->
<div v-if="condition" key="a">Yes</div>
<div v-else key="b">No</div>

Combined

In Vue 2.x, the < template > tag cannot have a key. However, you can set a key for each child node.

1
2
3
4
5
<!-- Vue 2.x -->
<template v-for="item in list">
<div :key="item.id">...</div>
<span :key="item.id">...</span>
</template>

In Vue 3.x’key ‘should be set on the’ < template > 'tag.

1
2
3
4
5
<!-- Vue 3.x -->
<template v-for="item in list" :key="item.id">
<div>...</div>
<span>...</span>
</template>

Similarly, when using < template v-for > there are sub-nodes that use v-if, the key should be set on the < template > tag instead.

1
2
3
4
5
6
7
8
9
10
11
<!-- Vue 2.x -->
<template v-for="item in list">
<div v-if="item.isVisible" :key="item.id">...</div>
<span v-else :key="item.id">...</span>
</template>

<!-- Vue 3.x -->
<template v-for="item in list" :key="item.id">
<div v-if="item.isVisible">...</div>
<span v-else>...</span>
</template>

v-if

  • ** Incompatibility **: When both apply to the same element, ‘v-if’ will have higher priority than’v-for '.

v-bind

2.x

In 2.x, if an element defines both’v-bind = “object” ‘and an identical separate property, then this separate property will always override the binding in’object’.

1
2
3
4
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- result -->
<div id="red"></div>

3.x

In 3.x, if an element defines both’v-bind = “object” ‘and an identical separate property, then the order in which the bindings are declared determines how they are merged. In other words, developers now have more control over the behavior of their desired merges than if they always wanted separate properties to override what was defined in’object’.

1
2
3
4
5
6
7
8
9
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div>
<!-- result -->
<div id="blue"></div>

<!-- template -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- result -->
<div id="red"></div>

v-for

In Vue 2, the’ref ‘attribute used in’v-for’ will populate the corresponding ‘$refs’ property with a ref array. This behavior becomes ambiguous and inefficient when there are nested’v-for '.

In Vue 3, such usage will no longer automatically create arrays in $ref. To get multiple ref from a single binding, bind ref to a more flexible function (this is a new feature):

1
<div v-for="item in list" :ref="setItemRef"></div>

Combined with optional API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export default {
data() {
return {
itemRefs: []
}
},
methods: {
setItemRef(el) {
this.itemRefs.push(el)
}
},
beforeUpdate() {
this.itemRefs = []
},
updated() {
console.log(this.itemRefs)
}
}

Combined with modular API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { ref, onBeforeUpdate, onUpdated } from 'vue'

export default {
setup() {
let itemRefs = []
const setItemRef = el = > {
itemRefs.push(el)
}
onBeforeUpdate(() => {
itemRefs = []
})
onUpdated(() => {
console.log(itemRefs)
})
return {
itemRefs,
setItemRef
}
}
}

Note:

  • itemRefs doesn’t have to be an array: it can also be an object whose ref is set by the iterated key.
    If desired, itemRef can also be responsive and can be listened to.