Superfast Introduction to Vue 3
Praveen Puglia
@praveenpuglia
praveenpuglia.com
@vuehyd
hyd.vue.community
The Struggle
Reactivity
Reactivity
let internalValue = data.name;
const dep = new Dep();
Object.defineProperty(data, 'name', {
get() {
// name is accessed.
dep.depend();
return internalValue;
},
set(newVal) {
// name is updated.
internalValue = newVal;
dep.notify();
}
});
export default {
data() {
name: "Vue Hyderabad"
},
computed: {
message() {
return `Hello! ${this.name}`;
}
}
}
👎 Reactivity caveats
Cannot detect changes to array items or object property value changes
Cannot declare reactive properties later...sort of!
var vm = new Vue({
data: {
a: 1
}
})
// `vm.a` is now reactive
vm.b = 2
// `vm.b` is NOT reactive
// Array item update via [index] notation
vm.items[indexOfItem] = newValue
// Add or remove a property in an object
delete vm.user.firstName
vm.user.age = 28
😕 Half baked solutions
Cannot detect changes to array items or object property value changes
Cannot declare reactive properties later...sort of!
// Can't add to root level anymore
// But we can in nested object.
Vue.set(vm.someObject, 'b', 2)
Vue.set(vm.items, indexOfItem, newValue)
Go Immutable
Code organization
const vm = new Vue({
props: {...},
data() {...},
methods: {...},
computed: {...},
filters: {...},
watch: {...},
created() {...},
mounted() {...},
...,
...
})
Code 😳r...MESS
const vm = new Vue({
props: {
},
data() {
},
methods: {
},
computed: {
},
filters: {
},
watch: {
},
created() {
},
mounted() {
}
})
TypeScript
- Large projects and teams
 - Make the most out of GraphQL Stack
 - Better tooling, IDE support
 - All the benefits of a Type System.
TypeScript IN VUE
- Vue was never built using TypeScript or for it.
 - A lot of magic happens on `this`, the Vue instance.
 - Large objects aren't really that type friendly, such as the Options API
 - Makes it hard to work with Plugins, Third Party components / tools.
👎 Half baked solutions
import { Vue, Component, Prop } from "vue-property-decorator";
@Component
export default class AwesomeComponent extends Vue {
@Prop({
type: Boolean
})
isStupid!: boolean;
dataProperty = 'This is reactive';
anotherDataProperty = 'This is reactive too';
get computedValue() {...}
created() {...}
}
The Relief
It's Not GOING To BE LIKE ANGULAR
New apis are purely opt-in
REACTIVITYÂ
USING PROXIES
const target = {};
const targetProxy = new Proxy(target, {
get: function(theTarget, prop) {
return prop === 'age' ?
`Your age is ${theTarget[prop]} years`
: theTarget[prop];
}
});
target.age = 20
target.age // 20
targetProxy.age // Your age is 20 years
- Double the speed, immediately
 - Half the memory footprint
 - No need of Vue.set or patching Array.prototype.push / pop
 - You get the benefits for free
REACTIVITYÂ
USING PROXIES
vueschool.io
API
COMPOSITION
-
All the time people had spent learning Vue had been wasted given everything was about to change.
 -
The Vue Core team had suddenly implemented a huge breaking change without any consultation.
 -
Vue is turning into React!
 -
No, Vue is turning into AngularJS/Angular!
 - All HTML now needs to be written as a giant string!
- Completely Opt-in
 - You don't need to rewrite your entire codebase
 - The entire changes went through an RFC process
API
COMPOSITION
import { reactive } from 'vue'
const state = reactive({
count: 0
})
import { reactive, watch } from 'vue'
const state = reactive({
count: 0
})
watch(() => {
document.body.innerHTML = `count is ${state.count}`
})
new Vue({
data() {
return {
count: 0
}
}
})
import { reactive, watch } from 'vue'
const state = reactive({
count: 0
})
function increment() {
state.count++
}
const renderContext = {
state,
increment
}
watch(() => {
// hypothetical internal code, NOT actual API
renderTemplate(
`<button @click="increment">{{ state.count }}</button>`,
renderContext
)
})
API
COMPOSITION
import { reactive, computed } from 'vue'
const state = reactive({
count: 0
})
const double = computed(() => state.count * 2)
// Access computed
console.log(double.value)
<!-- Automatic Ref Unwrapping -->
{{ double }}
API
COMPOSITION
<template>
<button @click="increment">
Count is: {{ state.count }},
double is: {{ state.double }}
</button>
</template>
<script>
import { reactive, computed } from 'vue'
export default {
setup() {
const state = reactive({
count: 0,
double: computed(() => state.count * 2)
})
function increment() {
state.count++
}
return {
state,
increment
}
}
}
</script>
API
COMPOSITION
<script>
import { reactive, computed } from 'vue';
import { useCounter } from '@/utils';
export default {
setup() {
const {state, increment} = useCounter();
return {
state,
increment
}
}
}
</script>
WHY DO ALL THIS?
API
COMPOSITION
Extracting logic and much better composition & reuse.
useSearch() useSort() usePagination() useIntersectionObserver() useLocalStorage() ... vue-use-web
API
COMPOSITION
- Be able to use Vue's reactivity system without a Vue instance
 - Much wider application area
 - Tree-shaken internals for smaller builds
 - Much improved TS support because of internals being mere functions
 - Plugins without polluting global Vue object.
API
COMPOSITION
VDom rewrite
<div>
<p>This is static</p>
<p>This is static as well</p>
<p>This is {{dynamicVariable}}</p>
<p>This is static after dynamic</p>
</div>
Before
Re-render everything, even the static parts.
After
Only re-renders the paragraph at line 4. Static parts get hoisted and never trigger changes
VDom rewrite
Monomorphic VNode Creation
function add(a: any, b: any) {
return a + b;
}
add(1, 2) // 3
add('a', 'b') // ab
function add(a: number, b: number) {
return a + b;
}
function concat(a: string, b: string) {
return a + b;
}
add(1, 2) // 3
concat('a', 'b') // ab
Multiple root nodes
<template>
<li v-for="item of items" :key="item.id">
<icon :name="item.icon"></icon>{{item.title}}
</li>
</template>
Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.
Vue 2
Vue 3
-
v-if
-
v-for
- multiple nodes
- works for any component
SIMPLER custom inputs
<BaseInput
placeholder="John Doe"
label="First Name"
v-model="firstName"
@input="handleFirstName"
/>
<template>
<label>
{{ label }}
<input
v-bind="$attrs"
v-on="$listeners"
@input="$emit('input', $event.target.value)"
/>
</label>
</template>
<script>
export default {
props: ["label"],
inheritAttrs: false
};
</script>
SIMPLER custom inputs
<BaseInput
placeholder="John Doe"
label="First Name"
v-model="firstName"
@input="handleFirstName"
/>
<template>
<label>
{{ label }}
<input
v-bind="$attrs" />
</label>
</template>
<script>
export default {
props: ["label"]
};
</script>
Everything is $attrs
-
Events : v-on:input => on-input
 -
v-model : value / on-update:value
 - Multiple v-model : v-model:startDate, v-model:endDate
<InviteeForm
v-model:name="inviteeName"
v-model:email="inviteeEmail"
/>
<DatePicker
v-model:startDate="startDate"
v-model:endDate="endDate"
/>
And there's More
- In Built Portals
 - All slots are scoped slots & compiled as functions
 - New custom directives API
 - Suspense
 - Improved native support
 - Thorough migration guide
READ ON
-
https://vueschool.io/articles/vuejs-tutorials/exciting-new-features-in-vue-3/
 -
https://vue-composition-api-rfc.netlify.com/
 -
https://www.vuemastery.com/courses/vue-3-essentials/why-the-composition-api/
 -
https://vueschool.io/articles/vuejs-tutorials/faster-web-applications-with-vue-3/
 - https://alligator.io/vuejs/common-gotchas/
Praveen Puglia
@praveenpuglia
praveenpuglia.com
@vuehyd
hyd.vue.community
Superfast Introduction to Vue 3
By Praveen Puglia
Superfast Introduction to Vue 3
- 1,330