Vue.js v3.
The new generation
of front end frameworks
Igor Sheko
lead Of FRONT-END Team
at Voximplant
Frameworks Generations
Generation Zero
-
SNOWFLAKES SCRIPTS
-
VISUAL EFFECTS
-
UNNECESSARY SCRIPTING
First generation
-
AJAX ERA
-
FIRST BUSINESS LOGIC
-
PLUGINS FOR ADDING two NUMBERS
-
REAL BUSINESS LOGIC IN JAVA AND FLASH
BEFORE THIS MILESTONE WE WERE MORE HTML CODERS
THAN JS DEVELOPERS
Second generation
-
COMPONENTS
-
COMPONENTS are EVERYWhere
-
REALLY BIG APPLICATIONS
-
REACTIVITY IN MOST CASES
Current generation
WHAT A bunch OF obstacles!
-
just Dawning
Ancient times of the Internet
Now
Java Script was born
September 1995
jQuery released
August 2006
AngularJS
October 2010
React
May 2013
ES6
June 2015
WHAT A BUNCH OF OBSTACLES!
-
JUST DAWNING
-
LeGACY BROWSERS
Ancient times of the Internet
Now
Java Script was born
September 1995
jQuery released
August 2006
AngularJS
October 2010
React
May 2013
IE 6 lost support
January 2016
Ancient times of the Internet
Now
Java Script was born
September 1995
jQuery released
August 2006
AngularJS
October 2010
React
May 2013
IE 6 lost support
January 2016
IE 11 lost support
October 2025
HOW REACTIVITY
WORKS TODAY
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
With Proxy API
LESS IS BETTER
data = new Proxy(data_without_proxy, { // Override data to have a proxy in the middle
get(obj, key) {
deps.get(key).depend(); // <-- Remember the target we're running
return obj[key]; // call original data
},
set(obj, key, newVal) {
obj[key] = newVal; // Set original data to new value
deps.get(key).notify(); // <-- Re-run stored functions
return true;
}
});
Evan You on Proxies
2.5
3.x
Rendering of 3000 state-full components
Half the memory usage
Half the rendering time
Twice the user happiness
WHAT A BUNCH OF OBSTACLES!
-
JUST DAWNING
-
LeGACY BROWSERS
-
FALSE SIMPLICITY
import { mapGetters } from 'vuex';
export default {
// ...
computed: {
...mapGetters([
'doneTodosCount',
'anotherGetter'
// ...
])
}
};
/**
* Reduce the code which written in Vue.js for getting the getters
* @param {String} [namespace] - Module's namespace
* @param {Object|Array} getters
* @return {Object}
*/
export const mapGetters = normalizeNamespace((namespace, getters) => {
const res = {}
normalizeMap(getters).forEach(({ key, val }) => {
// The namespace has been mutated by normalizeNamespace
val = namespace + val
res[key] = function mappedGetter () {
if (namespace && !getModuleByNamespace(this.$store, 'mapGetters', namespace)) {
return
}
if (process.env.NODE_ENV !== 'production' && !(val in this.$store.getters)) {
console.error(`[vuex] unknown getter: ${val}`)
return
}
return this.$store.getters[val]
}
// mark vuex getter for devtools
res[key].vuex = true
})
return res
})
WHAT A BUNCH OF OBSTACLES!
-
JUST DAWNING
-
LeGACY BROWSERS
-
FALSE SIMPLICITY
-
COPY|PASTE DRIVEN DEVELOPMENT
COPY|PASTE DRIVEN DEVELOPMENT
Vue.js—Difference between v-model and v-bind
Question:
- 157 point for the question
- 341 point for the answer
- 53 stars
Numbers:
Documentation:
<input v-model="searchText">
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
Remember that:
does the same thing as:
WHAT AN obstacles!
-
JUST DAWNING
-
LeGACY BROWSERS
-
FALSE SIMPLICITY
-
COPY|PASTE DRIVEN DEVELOPMENT
-
SOURCE CODE DUPLICATION
PROBLEMS GALORE
CODE REUSAGE
AND
THE Single responsibility principle
Action
State
View
Component
Mutation
state
Getter
User
Auth
Conversations
Notifications
Middleware
// define a mixin object
var myMixin = {
created: function () {
this.hello()
},
methods: {
hello: function () {
console.log('hello from mixin!')
}
}
}
// define a component that uses this mixin
var Component = Vue.extend({
mixins: [myMixin]
})
var component = new Component() // => "hello from mixin!"
// define a mixin object
var myMixin = {
created: function () {
this.hello()
},
methods: {
hello: function () {
console.log('hello from mixin!')
}
}
}
// define a mixin object
var myMixinTwo = {
methods: {
hello: function () {
console.log('hello from mixin 2!')
}
}
}
// define a component that uses this mixin
var Component = Vue.extend({
mixins: [myMixin, myMixinTwo]
})
var component = new Component() // => "hello from mixin!"
MIXIN's PROBLEMS
-
NAME CLASHING
-
REAL CODE DUPLICATE
Component isolation. THE DARK SIDE.
-
BAD DATA REUSAGE
-
IGNORED NATIVE inheritance
-
MAKE PEOPLE THINKING INSIDE THE BOX
BUT IT IS SO DAMN FAST!
IS IT?
<template>
<div class="content">
<p class="text">Lorem ipsum</p>
<p class="text">Lorem ipsum</p>
<p class="text">{{message}}</p>
<p class="text">Lorem ipsum</p>
<p class="text">Lorem ipsum</p>
</div>
</template>
function render() {
const children = []
for (let i = 0; i < 5; i++) {
children.push(h('p' , {
class: 'text'
}, i === 2 ? this.message : 'Lorum ipsum'))
}
return h('div', { id: 'content' }, children)
}
<p>Hello {name}!</p>
p(changed, ctx){
if(changed.name){
set_data(t1, ctx.name);
}
}
<template>
<div class="content">
<p class="text">Lorem ipsum</p>
<p class="text">Lorem ipsum</p>
<p class="text">{{message}}</p>
<p class="text">Lorem ipsum</p>
<p class="text">Lorem ipsum</p>
</div>
</template>
PERFORMANCE BENCHMARK
- v-for with 1000 iterations
- Inside each iteration:
- 12 DOM elements nested 3 level deep
- 2 dynamic class bindings
- 1 dynamic text interpolation
- 1 dynamic id attribute binding
- Update all dynamic bindings, take average of 100 runs
2.6.10
3.0-proto-2019-06
36 ms
5.44 ms
more THAN 6x faster
(in this benchmark)
const state = Vue.observable({ count: 0 })
const Demo = {
render(h) {
return h('button', {
on: { click: () => { state.count++ }}
}, `counter — ${state.count}`)
}
}
The Composition API
<template>
<form @submit="handleSubmit">
<label>
<span>Note:</span>
<input v-model="currentNote" @input="handleNoteInput">
</label>
<button type="submit">Send</button>
</form>
</template>
<script>
import { ref, watch } from "vue";
export default {
props: ["divRef"],
setup(props, context) {
const currentNote = ref("");
const handleNoteInput = e => {
const val = e.target.value && e.target.value.toUpperCase()[0];
const validNotes = ["A", "B", "C", "D", "E", "F", "G"];
currentNote.value = validNotes.includes(val) ? val : "";
};
const handleSubmit = e => {
context.emit("note-sent", currentNote.value);
currentNote.value = "";
e.preventDefault();
};
return {
currentNote,
handleNoteInput,
handleSubmit,
};
}
};
</script>
export const outerCount = ref(0)
// где-то в компоненте Example.vue
<template>
<button @click="onClick">счётчик — {{count}}</button>
<template>
<script>
import {outerCount} from "foo.js"
const Example = {
setup(props, { attrs }) {
const count = outerCount;
function onClick() {
count.value++;
}
}
}
</script>
const obj = reactive({ count: 0 })
const count = ref(0)
ref FUncTion
vs
reactive FUNCTION
// стиль 1: отдельные значения
let x = 0
let y = 0
function updatePosition(e) {
x = e.pageX
y = e.pageY
}
// --- сравниваем с ---
// стиль 2: объект
const pos = {
x: 0,
y: 0
}
function updatePosition(e) {
pos.x = e.pageX
pos.y = e.pageY
}
function useMousePosition() {
const pos = reactive({
x: 0,
y: 0
})
// ...
return pos
}
export default {
setup() {
// реактивность потеряна!
const { x, y } = useMousePosition()
return {
x,
y
}
// реактивность потеряна!
return {
...useMousePosition()
}
// реактивность не потеряна
return {
pos: useMousePosition()
}
}
}
function useMousePosition() {
const pos = reactive({
x: 0,
y: 0
})
// ...
return toRefs(pos)
}
// x & y are now refs!
const { x, y } = useMousePosition()
const count = ref(0)
watch(() => console.log(count.value))
// -> logs 0
setTimeout(() => {
count.value++
// -> logs 1
}, 100)
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
const stop = watch(() => { /* ... */ })
// later
stop()
SORRY, NO CONCLUSION AT ALL
BUT ONE MORE THING...
// event-bus.js
import Vue from 'vue';
const EventBus = new Vue();
export default EventBus;
//vue/src/core/instance/index.js
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
export default class PubSub{
private listeners:Map<string,EventListener[]> = new Map();
addEventListener(event:string,listener:EventListener){
const listeners = this.listenerts.get(event)||[];
this.listenerts.set([...listeners, listener]);
}
removeEventListener(event:string,listener?:EventListener){
if(!listener)
this.listenerts.remove(event);
this.listenerts.set(this.listeners.filter(item=>item!==listener]);
}
dispatch(event:string, data?:any){
const listeners = this.listenerts.get(event)||[];
listeners.forEach(item=>item.handleEvent(data));
}
}
Thank YOU!
www.facebook.com/igor.sheko
@iRbisaDm
IGOR SHEKO
Vue.js v3. The new generation of front-end frameworks.
By Igor Sheko
Vue.js v3. The new generation of front-end frameworks.
Just imagine a whole new world with the fastest responsive and lightweight web applications you've ever seen. Front end developers don't only write code for %frameworkName% but also know what they do and how it works. A lot of them. Let's discuss how the new front-end generation brings part of this magick world to reality.
- 344