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
Fun With Flags - praveenpuglia.com/fun-with-flags
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