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