On custom components v-model prop and event default names are changed.
- prop: value -> modelValue
- event: input -> update:modelValue
Vue 2
<ChildComponent v-model="pageTitle" /> <!-- would be shorthand for: --> <ChildComponent :value="pageTitle" @input="pageTitle = $event" />
Vue 3
<ChildComponent v-model="pageTitle" /> <!-- would be shorthand for: --> <ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event" />
Vue 2
// ChildComponent.vue export default { model: { prop: 'title', event: 'change' }, props: { // this allows using the `value` prop for a different purpose value: String, // use `title` as the prop which take the place of `value` title: { type: String, default: 'Default title' } } } <ChildComponent v-model="pageTitle" /> <!-- would be shorthand for: --> <ChildComponent :title="pageTitle" @change="pageTitle = $event" />
Vue 3
<ChildComponent v-model:title="pageTitle" /> <!-- would be shorthand for: --> <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
v-bind.sync and component model option are removed and replaced with an argument on v-model.
Vue 2
// ChildComponent.vue this.$emit('update:title', newValue) <!-- ParentComponent.vue --> <ChildComponent :title.sync="pageTitle" /> <!-- would be shorthand for: --> <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
Vue 3
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" /> <!-- would be shorthand for: --> <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" :content="pageContent" @update:content="pageContent = $event" />
Keys are no longer necessary on v-if, v-else, v-else-if branches, since Vue now automatically generates unique keys. If provided, then each branch must use a unique key.
Vue 2
<div v-if="condition" key="a">Yes</div> <div v-else key="a">No</div>
Vue 3
<div v-if="condition">Yes</div> <div v-else>No</div> <!-- or --> <div v-if="condition" key="a">Yes</div> <div v-else key="b">No</div>
<template v-for> key should be placed on the <template> tag (rather than on its children).
Vue 2
<template v-for="item in list"> <div :key="'heading-' + item.id">...</div> <span :key="'content-' + item.id">...</span> </template>
Vue 3
<template v-for="item in list" :key="item.id"> <div>...</div> <span>...</span> </template>
If used on the same element, v-if will have higher precedence than v-for.
Vue 2
<li v-for="todo in todos" v=if="!todo.isComplete"> {{ todo.name }} </li>
Vue 3
<template v-for="todo in todos"> <li v-if="!todo.isComplete"> {{ todo.name }} </li> </template>
Order of bindings for v-bind now affects the rendering result.
Vue 2
<!-- template --> <div id="red" v-bind="{ id: 'blue' }"></div> <!-- result --> <div id="red"></div> <!-- template --> <div v-bind="{ id: 'blue' }" id="red"></div> <!-- result --> <div id="red"></div>
Vue 3
<!-- 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>
The native modifier for v-on has been removed.
Vue 2
<ChildComponent v-on:close="handleComponentEvent" v-on:click.native="handleNativeClickEvent" />
Vue 3
// ChildComponent.vue export default { emits: ['close'] } <!-- ParentComponent.vue --> <ChildComponent v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" />