Understanding

Vue's reactivity system

by building one

Conversational Intelligence Platform

 

Quality Management & More

 

Speech-to-Text for Mixed Languages & Multi-parties

 

Tone & Emotions

 

Intent & Entity Detection

Praveen Puglia

@praveenpuglia

Reactivity

🤬

😐

👊

😵

🎁

🤩

Not happening!

Totally Possible

*secretly wishes that Devfest organizers are listening to all this*

  • What made the text change from "Follow" to "Following" ?
     
  • What changed the appearance of the button?
// Initialize
isFollowing = false;

followButtonText  = isFollowing ? 'Following' : 'Follow';

// At a later point in time
// CALL THE API TO FOLLOW

// Om phat swaha! ✋
isFollowing = true;

FRAMEOWRKS

Build / Update UI from Data Model

Make UI react to Data Changes automatically

Without touching the actual DOM...sort of

🙀

let number = 10;
let square = number * number
console.log(square); // 100
number = 20
console.log(square); // 100 NOT 400
// Initialize
isFollowing = false;

followButtonText  = isFollowing ? 'Following' : 'Follow';

// At a later point in time
// CALL THE API TO FOLLOW

// Om phat swaha! ✋
isFollowing = true;

🙋‍♂️We want a spreadsheet

Kyun.js

addEventListener()

🥁

🥁

🥁

🥁

🥁

🥁

document.body.addEventListener('mousemove', 
({ x, y }) => {
  console.log({ x, y });
});
> Object {x: 576, y: 281}
> Object {x: 577, y: 282}
> Object {x: 578, y: 283}
> Object {x: 579, y: 284}
addEventListener()
  •     stores the handlers somewhere.
     
  •     something says that the event has triggered.
     
  •     executes all handlers when the event triggers.

🥁

🥁

🥁

🥁

🥁

🥁

🤔

// Storage
const handlers = [];

let number = 10;
let square = 0

// Mystery
let currentHandler = null;

// The one that stores the handler
function addPropertyHandler() {
  handlers.push(currentHandler)
}

// The one that executes all handlers
function notifyHandlers() {
  handlers.forEach(handler => handler())
}
currentHandler = () => {
  square = number * number;
}

// store the handler
addPropertyHandler();

currentHandler();

console.log(square); // 100

number = 20;

notifyHandlers();

console.log(square); // 400
number = 10;
notifyHandlers();
console.log(square); // 100

number = 20;
notifyHandlers();
console.log(square); // 400

number = 30;
notifyHandlers();
console.log(square); // 900
number = 10;
square; // 100

number = 20
square; // 400

🏁

addEventListener()

({x, y}) => {
  console.log({x, y})
}
mousemove
() => {
  alert('Clicked!')
}
click

Our Solution

() => {
  square = number * number;
}
handlers
() => {
  msg = `Hello ${name}`;
}
  • 🎢 Scale the solution.
  • 📦 Store automatically.
  • 🔔 Notify automatically.
class Dep {
  constructor() {
    this.handlers = new Set();
  }
  depend() {
    // same as addPropertyListener
    if(currentHandler) {
      this.handlers.add(currentHandler);
    }
  }
  
  notify() {
    // same as notifyHandlers
    this.handlers.forEach(
      handler => handler()
    );
  }
}

🎢

let currentHandler = null;
let dep = new Dep();

let number = 10;
let square = 0

currentHandler = () => {
  square = number * number;
}

dep.depend();

currentHandler();
console.log(square); // 100

number = 20;
dep.notify();

console.log(square); // 400

🎢

🎢

() => {
  square = number * number;
}
number
() => {
  message = `Hello ${name}`;
}
name
depend() {
  // same as addPropertyListener
  if(currentHandler) {
    this.handlers.add(currentHandler);
  }
}

JS can execute

only one function

at a time

let currentHandler = null;

currentHandler = () => {
  square = number * number
}

dep.depend();

currentHandler();

currentHandler = null;
function watcher(handler) {
  currentHandler = handler;
  dep.depend();
  currentHandler();
  currentHandler = null;
}

watcher(() => {
  square = number * number;
});

🎢

But where is the attach instance of Dep part?

🎢

We need a way to know when our data was touched

🎢

Object.defineProperty()

let data = {};

Object.defineProperty(data, 'a', {
  get() {
    console.log("data.a was accessed");
  },
  set() {
    console.log("data.a was modified");
  }
})

Use data Object

// FROM
let number = 10;

// TO
let data = {number: 10}

🎢

Make one reactive

let internalValue = data.number;
let dep = new Dep();
Object.defineProperty(data, 'number', {

  get() { // number is accessed.
    dep.depend();
    return internalValue;
  },

  set(newVal) { // number is updated.
    internalValue = newVal;
    dep.notify();
  }
})

🎢

📦

🔔

number = 10;
square; // 100

number = 20
square; // 400

🏁

set() {
  dep.notify();
}

Make all reactive

Object.keys(data).forEach(key => {
  let internalValue = data[key];
  const dep = new Dep();
  Object.defineProperty(data, key, {

    get() { 
      dep.depend();
      return this.internalValue;
    }

    set(newVal) { 
      this.internalValue = newVal;
      dep.notify();
    }
  })
});

🎢

📦

🔔

Converter

let currentHandler = null;

function watcher(handler) {
  currentHandler = handler;
  dep.depend();
  currentHandler();
  currentHandler = null;
}

watcher(() => {
  square = number * number;
});
let currentHandler = null;

function watcher(handler) {
  currentHandler = handler;

  currentHandler();
  currentHandler = null;
}

watcher(() => {
  square = number * number;
});
get() {
  dep.depend();
}

Before

After

  • 🎢 Scale the solution. ✅
  • 📦 Store automatically. ✅
  • 🔔 Notify automatically. ✅
number = 10;
square; // 100

number = 20
square; // 400

🏁

🎉

🎈

Kyun.js

But how does the render thingy?

template

</>

render

()

watcher

👁

A

Framework

Is 

Born

🙍‍♂️Caveats

Must declare reactive properties beforehand

🙍‍♂️

new Vue({
  data() {
    return {
      number : 10
    }
  }
})

Adding reactive property

🙍‍♂️

Vue.set(vm.someObject, 'b', 2)
// OR
this.$set(this.someObject, 'b', 2)

Adding / removing items in arrays

🙍‍♂️

this.todos[this.todos.length] = "do nothing"; // 👎
this.todos.push("do nothing"); // 👍

this.todos = [...this.todos, "do nothing"];

🦹‍♀️PROXIES FOR THE FUTURE

  • No need to use $set() to add a new reactive property.
     
  • Setting arr[key] = 'wow' will also trigger change detection.
     
  • Double the speed. Half the memory.
     
  • With the exact same API that we all love.

🦹‍♀️

We now have better understanding of Vue internals & the problems associated with it.

😇

Praveen Puglia

@praveenpuglia

Understanding Vue's Reactivity System by Building One

By Praveen Puglia

Understanding Vue's Reactivity System by Building One

  • 2,123