Vue.js Composition API

Cristian Salcescu
Frontend Essentials
3 min readSep 8, 2020

The Vue.js Composition API provides a new way of managing reactivity. It is made of a set of Reactive API functions and plus the facility to register lifecycle hooks using imported functions.

In the Vue.js Composition API book, we are taking a look at how to implement this Reactive API from scratch. Then we will implement a master-details functionality.

Chapter 01: Building a Reactive API

We start our learning journey by first understanding what reactivity is how is implemented in Vue 3. Imagine we have a simple state object and a render function.

const state = {
message : 'This is a message'
};
function render(state){
document
.getElementById('UI')
.innerHTML =
`<span>${state.message}</span>`;
}

Invoking the render(state) function displays the message in the UI.

render(state);

Now, suppose we modify the message property of the state object.

state.message = 'A new message';

The change of the state object is not reflected in the UI. If we want that to happen we need to invoke again the render function.

state.message = 'A new message';
render(state);

Proxy

We need a way to detect object changes and call the render function. The Proxy object can help achieve that.

The proxy object defines custom behavior for operations like getting or setting a property. The next example shows a proxy calling the render function on property changes.

const reactiveHandler = {
set: function(obj, prop, value) {
obj[prop] = value;
render(obj);
return true;
}
};
const state = new Proxy({
message: 'This is a message'
}, reactiveHandler);

The reactiveHandler provides a handler for setting an object property. The handler changes the property, calls render with the modified object and returns trueindicating a successful operation.

reactive()

Reactivity in Vue refers to the process of automatically updating the UI when the state object changes.

We can make an object reactive by simply using a new utility function wrapping the proxy call.

function reactive(obj){
return new Proxy(obj, reactiveHandler);
}
const state = reactive({
message: 'This is a message'
});

The reactive function takes an object and returns a new proxy object wrapping it. All the interactions with the object, like setting and getting data, go through the proxy handler.

At this point, there is no need to manually call the render function when doing a change on the stateobject. The following change is reflected in the UI.

state.message = 'A new message';

watchEffect()

This solution has a drawback. It explicitly states the function to be called. We want to be able to detect this subscriber function without explicitly invoking it in the proxy definition.

JavaScript is a single-threaded language. This means that a single function can execute at a specific moment. We can create a utility function that executes this function but also stores it in a variable.

Consider the following watchEffect utility function.

let currentFunc = null;

function watchEffect(f){
currentFunc = f;
f();
currentFunc = null;
}

watchEffect takes a function f. It stores the function in the currentFunc variable, giving us access to the currently executing function. Then it executes the ffunction and resets the currentFunc variable to null.

Instead of executing the render(state) directly, we can call it using the watchEffect utility.

watchEffect(() => {
render(state);
});

At this point, we have access to the currently executing function in the reactiveHandler handler.

The get handler returns the requested property and saves the currently executing function as the subscriber.

The set handler changes the property and calls the subscriber function registered in the get handler.

let subscriber = null;

const reactiveHandler = {
get: function (obj, prop) {
if(currentFunc != null){
subscriber = currentFunc;
}
return obj[prop];
},
set: function(obj, prop, value) {
obj[prop] = value;
subscriber(obj);
return true;
}
};

In this example, we don’t need to specify the function to be called when the state changes. The function is automatically detected by wrapping its call inside the watchEffect utility.

Click here to get a sample of the book.

--

--