Vue Mastery
Named and Scoped Slot Functional Attribute
COMPILER_SFC_FUNCTIONAL
VUE 2
<template functional>
<h1>{{ text }}</h1>
<ItemList>
</template>
<template slot="heading" slot-scope="slotProps">
<h1>My Heading for {{ slotProps.items.length }} items</h1> <script> Need to remove
</template> export default { this attribute.
</ItemList> props: ['text']
}
VUE 3 </script>
<ItemList>
<template v-slot:heading="slotProps">
<h1>My Heading for {{ slotProps.items.length }} items</h1>
</template> Four learning paths to expand your Vue skills.
</ItemList> Find yours here vuemastery.com/courses
Precedence Between v-if and v-for Mounted Container
COMPILER_V_IF_V_FOR_PRECEDENCE GLOBAL_MOUNT_CONTAINER
VUE 2
This used to run first. <html lang>
<ul> <head>...</head>
<li v-for="num in nums" v-if="num < 10">{{ num }}</li> <body>
</ul> <noscript></noscript>
<div id="app" data-v-app>
VUE 3 <div id="app" data-v-7ba
Now, this runs first. ...
<ul> </div> Now the mounted container
<li v-for="num in nums" v-if="num < 10">{{ num }}</li> </div> will still be there, so you might
</ul> ... get two container elements in
the rendered HTML.
SOLUTION
Use computed property.
<ul> <template> key
<li v-for="num in numsLower10">{{ num }}</li> COMPILER_V_FOR_TEMPLATE_KEY_PLACEMENT
</ul>
<template v-for="num in nums" :key="num.id">
<div :key="num.id">{{ num }}</div>
v-if Keys </template>
COMPILER_V_IF_SAME_KEY Need to move it to <template>
<ul>
<li v-for="num in nums"> Transition Classes
<span v-if="num < 10" :key="myKey">{{ num }}</span> (This deprecation has no warnings in the migration build.)
<span v-else class="high" :key="myKey">{{ num }}</span>
VUE 2 VUE 3
</li>
</ul> Rename
.v-enter .v-enter-from
Now you can’t use the same key for branches in the same Rename
conditional, either remove them all or use different keys. .v-enter .v-enter-from
Looking for more information? We have a blog series that you can dive into now.
Visit vuemastery.com/blog/migration
Vue Mastery
App Initialization (main.js)
GLOBAL_MOUNT / GLOBAL_EXTEND / GLOBAL_PROTOTYPE / GLOBAL_OBSERVABLE / GLOBAL_PRIVATE_UTIL
VUE 2 VUE 3
import Vue from "vue" import { createApp } from 'vue' Import the function
import App from './App.vue' import App from './App.vue' instead of the object.
import router from './router' import router from './router'
import store from './store' import store from './store'
Need to remove all uses of
the Vue object from the code. const app = createApp(App) Create the app instance
with the new function.
Vue.use(store) app.use(store)
Vue.use(router) app.use(router)
Vue.component('my-heading', { app.component('my-heading', {
props: [ 'text' ], props: [ 'text' ],
template: '<h1>{{ text }}</h1>' template: '<h1>{{ text }}</h1>'
}) })
VUE 2 VUE 3
const app = new Vue(App) No dollar sign prefix. Global APIs Instance APIs
app.$mount("#app"); app.mount('#app') Vue.component app.component
Vue.use app.use
Vue.config app.config
Vue.directive app.directive
Functional Component Vue.mixin app.mixin
COMPONENT_FUNCTIONAL
VUE 2 VUE 3
export default { import { h } from 'vue'
functional: true,
props: ['text'], const Heading = (props) => {
render(h, { props }) { return h('h1', {}, props.text)
return h('h1', {}, props.text) }
} Need to use a function
} Heading.props = ['text'] to create a functional
component.
export default Heading
Async Component
COMPONENT_ASYNC
v-for References
V_FOR_REF
import { defineAsyncComponent } from 'vue'
const MyComponent = defineAsyncComponent({ <template>
loader: () => import('./MyComponent.vue'), <ul>
... <li v-for="item in items" :key="item.id" ref="myNodes">
}) ...
Import and use the new function. </li>
</ul> Now this way of getting the references
</template> won’t work anymore.
...
mounted () {
Our blog series will show you a workaround for this. console.log(this.$refs.myNodes)
Find it here vuemastery.com/blog/migration }
Workaround
Vue Mastery
Lifecycle Hooks
OPTIONS_BEFORE_DESTROY / OPTIONS_BEFORE_DESTROY
VUE 2 VUE 3
export default { export default {
name: 'MyComponent', Rename name: 'MyComponent',
beforeDestroy() { beforeUnmount() {
... ...
}, Rename },
destroyed() { unmounted() {
... ...
}, },
... ...
} }
Custom Directive Hooks
CUSTOM_DIR
VUE 2 VUE 3
Vue.directive('my-directive', { app.directive('my-directive', {
created() { created() {
... ...
}, Rename },
bind() { beforeMount() {
... ...
}, Rename },
inserted() { mounted() {
... ...
}, },
update() { Removed beforeUpdate() { NEW
...
}, Rename },
componentUpdated() { updated() {
... ...
}, },
beforeUnmount() { NEW
...
Rename },
unbind() { ... } unmounted() { ... }
}) })
Lifecycle Events v-model Prop and Event
INSTANCE_EVENT_HOOKS COMPONENT_V_MODEL
VUE 2 VUE 2 VUE 3
<template>
<MyComponent @hook:mounted="foo"> Prop:
Rename
</template> value modelValue
Rename
Event: Rename
VUE 3
input update:modelValue
<template>
<MyComponent @vnode-mounted="foo">
</template>
Vue Mastery
Keyboard Codes Reactive Property Setters
V_ON_KEYCODE_MODIFIER GLOBAL_SET / GLOBAL_DELETE / INSTANCE_SET / INSTANCE_SET
VUE 2 Can’t use keycode anymore.
Vue.set Removed
<input type="text" v-on:keyup.112="validateText" /> Vue.delete Removed
vm.$set Removed
vm.$delete Removed
VUE 3 Use the key name instead.
Workaround
<input type="text" v-on:keyup.enter="validateText" />
Child Component References
INSTANCE_CHILDREN
v-bind sync modifier
COMPILER_V_BIND_SYNC
vm.$children Removed
VUE 2 Can’t use sync anymore.
Workaround
<MyComponent v-bind:title.sync="myString" />
Event Emitter
INSTANCE_EVENT_EMITTER
VUE 3 Use v-model instead.
vm.$on Removed
<MyComponent v-model:title="myString" />
vm.$off Removed
vm.$once Removed
Workaround
Native Event
COMPILER_V_ON_NATIVE Listeners
Need to remove the native modifier. INSTANCE_LISTENERS
<SpecialButton v-on:click.native="foo" /> vm.$listeners Removed
Workaround
Workarounds for these can be found at
vuemastery.com/blog/migration
Filters
FILTERS
VUE 2 VUE 3
<template> <template>
<p>{{ num | roundDown }}</p> <p>{{ numRoundedDown }}</p>
</template> </template>
Filters won’t work anymore. Use computed properties instead.
... ...
filters: { computed: {
roundDown(value) { numRoundedDown() {
return Math.floor(value) return Math.floor(this.num)
} }
}, },
Ready to level up your Vue skills?
Visit VueMastery.com to explore our library of Vue courses.