What’s new in Vue 3? The most interesting new features [Examples]

What has changed in Vue 3?

Composition API

Mixins

  • Can be organized by feature.
  • Conflict-prone: we can end up with some property name conflicts.
  • Unclear relationship how they interact.
  • Isn’t easily reusable in case we want to configure the mixin to be able to use it across other components.

Mixin factories

  • Easily reusable: so we can configure the code and have a more specific relationship of how mixins interact.
  • Cannot be dynamically generated because there’s no instance access at runtime.
  • Namespacing requires strong conventions.
  • They have implicit property additions: we have to look inside the mixin to figure out what properties it exposes.
  • Isn’t easily reusable in case we want to configure the mixin to be able to use it across other components.

Scoped slots

  • Addresses just about every downside of mixins.
  • Your configuration ends up in your template, which ideally should contain what we want to render.
  • Scoped slots increase indentation in our template, which can decrease readability.
  • Exposed properties are only available in the template.
  • Since we’re using 3 components instead of 1, it’s a bit less performant.
<template>
<div id="app">
<div class="content">
<h1>{{ title }}</h1>
<label for="title">
<input type="text" name="title" v-model="inputVal" />
</label>
<button @click="addTitle" class="add-btn">Add</button>
<button @click="clearInput">Clear</button>
<button @click="pressMe">Clear</button>
</div>
</div>
</template>
<script>
const exampleMixin = {
data() {
return {
someInfo: "This is our mixin!"
}
},
methods: {
pressMe() {
alert(this.someInfo);
}
}
}
export default {
mixins: [exampleMixin],
data() {
return {
inputVal: "",
title: ""
};
},
methods: {
clearInput() {
this.inputVal = "";
},
addTitle() {
this.title = this.inputVal;
}
}
};
</script>
}}
export default {
mixins: [exampleMixin],
data() {
return {
inputVal: "",
title: ""
};
},
methods: {
clearInput() {
this.inputVal = "";
},
addTitle() {
this.title = this.inputVal;
}
}
};
</script>
<template>
<div id="app">
<div class="content">
<h1>{{ title }}</h1>
<label for="title">
<input type="text" name="title" v-model="inputVal" />
</label>
<button @click="addTitle" class="add-btn">Add</button>
<button @click="clearInput">Clear</button>
</div>
</div>
</template>
<script>import { ref } from "vue";export default {
setup() {
const inputVal = ref("");
const title = ref("");
function clearInput() {
this.inputVal = "";
}
function addTitle() {
this.title = this.inputVal;
}
return { inputVal, title, clearInput, addTitle };
}
};
</script>
  • Writing less code, easier to pull a feature from your component into a function.
  • It builds on your existing skills since you’re already familiar with functions.
  • More flexible than Mixins and Scoped Slots
  • Intellisense, autocomplete, and typings already work in your code editor.
  • Good TypeScript support, so you’ll be able to generate new projects with the latest typeScript versions in Vue 3.
  • Requires learning a new low-level API to define composition functions.

Teleport

<teleport to="#app">
<div>Foo</div>
</teleport>
<teleport to="#app">
<div>Bar</div>
</teleport>
<div id="app">
<div>Foo</div>
<div>Bar</div>
</div>

Some of the components breaking changes

  • If we want to define events in a component that this component will emit to its parent, we can use a new option called “emit”, similar to existing props.
  • If we want to define an asynchronous component, we should use defineAsyncComponent function:
const asyncComponent = () => import ('./AsyncComponent.vue')
const asyncComponent = defineAsyncComponent(() => import ('./AsyncComponent.vue'))

Fragments

<template>
<div>
<header></header>
<section></section>
</div>
</template>
<template>
<header></header>
<section></section>
</template>

Filters were removed from Vue 3

<template>
<div id="app">
<span>{{ amount | usd }}</span>
</div>
</template>
<script>
export default {
data() {
return {
amount: 10
}
},
filters: {
usd(val) {
return val + '$'

}
}
}
</script>
<template>
<div id="app">
<span>{{ usd }}</span>
</div>
<template>
<script>
export default {
data() {
return {
amount: 10
}
},
computed: {
usd() {
return this.amount + '$'
}
}
}
</script>

Experimental Suspense feature

<Suspense>
<template #default>
<!-- component/component which makes an asynchronous call -->
</template>
<template >
<!-- Content to display when loading -->
Loading...
</template>
</Suspense>

Experimental state-driven CSS variables

<template>
<div id="app">
<div class="content">
<h1>Hello world!</h1>
<button @click="modalToggle">Click me</button>
</div>
<div v-if="showModal" class="modal">
<h2>Modal window</h2>
<button class="close-btn" @click="modalToggle">Close the window</button>
</div>
<div class="shadow"></div>
</div>
</template>
<script>
export default {
data() {
return {
displayShadow: "none",
showModal: false
};
},
methods: {
modalToggle() {
this.showModal = !this.showModal;
this.displayShadow = this.showModal ? "block" : "none";
}
}
};
</script>
<style>
.shadow {
display: v-bind(displayShadow);
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgb(46 46 46 / 31%);
z-index: 10;
opacity: 1;
}
</style>

Single file component <style scoped> changes

  • v-bind function is now supported by <style> tag
  • Instead of >>> and /deep/ combinators which are deprecated now we can use ::v-deep() or its shorter version :deep().
  • If we don’t want that slotted component was affected both by parent component styles and child’s scoped styles we can use ::v-slotted()/:slotted() pseudo-element. So in that way child scoped styles do not affect the slot component.

Multiple v-models

<template>
<div id="app">
<exampleForm v-model="person" />
</div>
</template>
<script>
export default {
data() {
return {
person: {
age: 20,
name: "Jan"
}
};
}
};
</script>
<template>
<div id="app">
<exampleForm v-model:age="person.age" v-model:name="person.name" />
</div>
</template>
<script>
export default {
data() {
return {
person: {
age: 20,
name: "Jan"
}
};
}
};
</script>

Lifecycle naming changes

<template> render changes:

Summary

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store