Vuejs Reactivity From Scratch
Tags: javascript, webdev, vue, learning
Published: 2017-08-17 16:51:15

---
title: Vuejs Reactivity From Scratch
published: true
description: Vuejs' reactivity seems like magic. How does it work?
tags: javascript, webdev, vuejs, learning
---
*This is a cross-posting from [my personal blog](https://log.koehr.in/2017/08/17/vuejs-reactivity-from-scratch).*

*The original version contains embedded code from JSFiddle, which doesn't work here. I therefor placed links to the respective JSFiddle pages instead.*

Vuejs is the star newcomer in the Javascript Framework world. People love how it makes complicated things very simple yet performant. One of the more exciting features is its seemingly magic reactivity. Plain data objects in components magically invoke a rerender when a property changes.

<a href="//jsfiddle.net/koehr/ajwchnko/embed/">LINK TO JSFIDDLE</a>

The button click invokes a function that just assigns a new value to a property. Still the template gets automagically rerendered. But we all know there is no fairydust involved, right? So how does it actually work?

The magic of getters and setters
--------------------------------

With the [ES5 standard](http://www.ecma-international.org/ecma-262/5.1/) JavaScript got lots of exciting new features. Some of them highly underrated and underused in my opinion. [Getters and setters](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6.1) are an example. If you never heard of them, I would recommend you to read [John Riesigs piece](https://johnresig.com/blog/javascript-getters-and-setters/) on them.

As soon as you know what getters and setters are: functions transparently called on every property access, you might already know where this goes. Boom! All the fairydust suddenly disappears.

Automatic getters and setters
-----------------------------

Now that we at least in theory know how Vuejs realises the template data magic, lets build it ourselves for the sake of full understanding!

Abstract: A function that gets an object and returns one with the properties replaced by getters and setters that, on call, rerender a template. So far so good. If you are really impatient, you can find [the final code in JSFiddle](https://jsfiddle.net/koehr/e2q9vme3/15/).

Lets start with a very simple approach:

<a href="//jsfiddle.net/koehr/e2q9vme3/embed/">LINK TO JSFIDDLE</a>

The function iterates through all object keys and creates a new object with getters and setters in their place. It could also directly manipulate the original object:

<a href="//jsfiddle.net/koehr/k72aoorc/1/embed/">LINK TO JSFIDDLE</a>

I personally don't like to manipulate the existing object and prefer the first way.

Introducing: Object.defineProperty
----------------------------------

Now before we go on with destroying our fantasies of fairydust computing, lets see if there is a more convenient way to what we've done for now. Here I introduce `Object.defineProperty`, which allows to set all possible attributes for an objects property. You can find a [detailed description on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty).

With this new knowlegde, the code can be made a bit more readable, by condensing everything into one call:

<a href="//jsfiddle.net/koehr/e2q9vme3/2/embed/">LINK TO JSFIDDLE</a>

All those underscores where pretty annoying anyways. I generally suggest you, to read more about `Object.defineProperty`. It extends the range of possibilities significantly!

Templating for the poor
-----------------------

To be able to rerender a component on data change, we should really introduce some components that can actually render and under the right circumstances rerender a template.

<a href="//jsfiddle.net/koehr/e2q9vme3/3/embed/">LINK TO JSFIDDLE</a>

This code describes a very simple component, that has a data object and a render function. If this is called, it replaces the `innerHTML` of the given content element with the rendered output. Neat! Lets make the data reactive!

Reactive Component
------------------

As a start, it should be enough to simply make the data property reactive:

<a href="//jsfiddle.net/koehr/e2q9vme3/4/embed/">LINK TO JSFIDDLE</a>

Yes, that seems to be good but it doesn't really update the template. Which becomes clear after a look at line 11-14: There is no render call ever. But `reactive` shouldn't know about component rendering, right? Lets try a more general approach with a callback:

<a href="//jsfiddle.net/koehr/e2q9vme3/5/embed/">LINK TO JSFIDDLE</a>

Yeah, that works and so on but it looks like we slowly stumble away from elegance in our code. The changes in `reactive()` seem to be okay, but that function bind monstrosity in line 31 is something we better hide from our parents. Lets introduce a component factory before we get kicked out or end up in self hatred:

<a href="//jsfiddle.net/koehr/e2q9vme3/6/embed/">LINK TO JSFIDDLE</a>

Cool! That works. The `createComponent()` function just does all the dirty work for us and returns a nice, reactive component, that is still just a simple object. If you have that code in a local setup and run something like `component.data.name = 'Ada Lovelace'`, then it will automagically rerender the template to show 'Hello Ada Lovelace'.

Nested Data structures
----------------------

All cool and hip stuff but what happens in the following scenario:

<a href="//jsfiddle.net/e2q9vme3/7/embed/">LINK TO JSFIDDLE</a>

Setting deeper nested properties (line 44,45) doesn't work at all. The reason is that the reactivity only works on the first nesting level of the data object. Now you could say: Easy, we just set the whole object at once:

<a href="//jsfiddle.net/e2q9vme3/8/embed/">LINK TO JSFIDDLE</a>

But this is not really what we strife for, isn't it? A way is needed, that makes all nested objects reactive in a recursive way. Surprisingly, this just needs a couple of lines:

<a href="//jsfiddle.net/e2q9vme3/9/embed/">LINK TO JSFIDDLE</a>

Only three lines (7-9) where added. They call `reactive()` on the given value in case it is an object. Now the nesting level doesn't matter anymore. REACTIVE ALL THE THINGS!!

Multiple Components
-------------------

Considering that components are usually very gregarious, what happens if we find a friend for our component? Will it blend? Erm I mean, react?

<a href="//jsfiddle.net/e2q9vme3/10/embed/">LINK TO JSFIDDLE</a>

It does! Hooray!

The attentive reader might have seen the change that sneaked into line 7: Because the type of array is object, an extra check has to be made here. Otherwise the array would be transformed to a plain object with keys 0, 1, etc.

But what happens now when we manipulate the Array directly?

<a href="//jsfiddle.net/e2q9vme3/11/embed/">LINK TO JSFIDDLE</a>

Bummer! Setting the whole array works as expected but manipulating it doesn't trigger any change.

Reactive Arrays
---------------

As described in [the caveats section](https://vuejs.org/v2/guide/list.html#Caveats) of the Vuejs guide about list rendering, there are several …well caveats with array reactivity. It writes:

    Due to limitations in JavaScript, Vue cannot detect the following changes to an array:
    1. When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue
    2. When you modify the length of the array, e.g. vm.items.length = newLength
Okay, fair enough. So what does happen in Vue to make Arrays reactive? Fairydust? Unfortunately yes. And this stuff is expensive! Nah, just kidding. Of course it is again no magic involved. I'm sorry my dear role-playing friends. What actually happens is that Arrays get their manipulating functions replaced by a wrapped version that notifies the component about changes. The source to this functionality is in [core/observer/array.js](https://github.com/vuejs/vue/blob/dev/src/core/observer/array.js).

Vuejs' approach is rather sophisticated but can be condensed down to something like what is seen in the first 24 lines here:

<a href="//jsfiddle.net/e2q9vme3/12/embed/">LINK TO JSFIDDLE</a>

So this is a fairly big chunk to digest. The new function `reactiveArray` starts with creating a copy of the original array (Remember? I don't like manipulating the original object). Then, for each function in the list of manipulative array functions the original is saved which is then replaced by a wrapper function. This wrapper function simply calls the render callback additionally to the original array function.

Now also `lipsumComponent.data.content` is not set directly anymore but uses the overwritten push method. Setting it directly wouldn't work. Fixing that leads us to the last step:

Reactivity on set
-----------------

For now the setter function didn't care about the value. If it would be a nested object, its children wouldn't be reactive. That means, if you set `data.x` to an object `{foo: 1}` and then change foo `data.x.foo++`, the template wouldn't rerender. This should be changed:

<a href="//jsfiddle.net/14/embed/">LINK TO JSFIDDLE</a>

Instead of setting the plain value, `reactive(value, callback)` is called in line 49. This small change works only up to a certain point on its own though. The function has to decide what to do with non-objects or arrays, which happens now as a first step in `reactive()`. A plain non-object (remember: arrays are objects) simply gets returned as it is (line 30), arrays will be returned in their reactive version (line 31).

Conclusion
----------

Congratulations! You made it this far or just skipped to read only the Conclusion, which is fine, I do that too sometimes.

In about [70 SLOC](https://jsfiddle.net/koehr/e2q9vme3/15/), we built a fully reactive component system. We made use of getters, setters and `Object.defineProperty` and learned, that I don't like to manipulate objects directly. Except for the last point, this should be valuable information that might become handy in future.

What else can be done you might ask? Vuejs' code is more sophisticated and handles some egde cases that I didn't mention for the sake of simplicity. For example if the yet to become reactive object has some getters and/or setters already, they would be overwritten by our simple solution. Vuejs' `defineReactive` uses `Object.getOwnPropertyDescription` to get a detailed information about the property it is going to wrap and incorporates existing getters and setters if applicable. It also ignores non-configurable (not meant to be changed at all) properties. How that works can be found [in the source code](https://github.com/vuejs/vue/blob/dev/src/core/observer/index.js#L140).