DEV Community

Cover image for Create Your Own Vue.js From Scratch - Part 5 (Bringing it all together)
Marc Backes
Marc Backes

Posted on

Create Your Own Vue.js From Scratch - Part 5 (Bringing it all together)

If you like this article, chances are you'd like what I tweet as well. If you are curious, have a look at my Twitter profile. 🚀

This post is the fifth part of a series called Create Your Own Vue.js From Scratch, where I teach you how to create the fundamentals of a reactive framework such as Vue.js. To follow this blog post, I suggest you first read the other parts of the series.

Roadmap 🚘

  1. Introduction (this post)
  2. Virtual DOM basics
  3. Implementing the virtual DOM & rendering
  4. Building reactivity
  5. Bringing it all together

Recap

In the last posts, we created our own virtual DOM and replicated a rudimentary Vue 2 reactivity.

For the virtual DOM, we created an engine that allows us to create virtual nodes, mount/unmount them to/from the actual DOM. The code can be found on Github.

For the reactivity, we built a dependency class, which we then use to detect changes in object property changes, we bound using Object.definePropert(). The code can be found on Github.

Preparation

First, we create a new HTML-file add a div with the ID app, and <script> tag.

Second, we copy the following functions from the VDOM example into the <script></script>-tag:

  • h
  • mount
  • unmount
  • patch

Third, we copy the following elements from the reactivity example into the <script>-tag:

  • Dep-class
  • watchEffect-function
  • reactive-function
  • activeEffect variable declaration

Writing our template

Let's assume we want to create a very simple click-counter like this one:

mini-vue-click-counter

We would need a structure like this:

<div id="app">
    <div id="container">
        <h1>NUMBER_OF_CLICKS</h1>
        <p>clicks</p>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

The #app-div is the mounting point for our application, so we just need to create a template for the .clickContainer and it's content. For this, we write a function that returns the template we will render to the DOM later, using the h-function from the VDOM:

function render(clickCount) {
    return h(
        'div',
        { class: 'container' },
        [h('h1', null, clickCount)],
        h('p', null, 'clicks'),
    )
}
Enter fullscreen mode Exit fullscreen mode

Create our reactive state

In this simple example, we only have one variable, so one property in our reactive state:

const state = reactive({
    count: 0,
})
Enter fullscreen mode Exit fullscreen mode

Make our VDOM reactive

We're almost done. What we need to do now is to call the watchEffect function, so we can react upon changes in the state.count property. There are two scenarios for this:

  1. It's the first time our callback function gets called: We need to mount the template to the DOM
  2. It's not the first time our callback function gets called: We need to patch() the node in the DOM

To differenciate between this, let's create a variable previousNode.

let previousVnode
watchEffect(() => {
    if (!previousVnode) {
        // No previous node. We must mount it to the DOM
    } else {
        // There is a previous node. We need to patch it (update)
    }
})
Enter fullscreen mode Exit fullscreen mode

For the first case, we create a new node with our render function and mount it to the DOM:

previousVnode = render(String(state.count))
mount(previousVnode, document.getElementById('app'))
Enter fullscreen mode Exit fullscreen mode

For the second case, we create a new node and patch it to the previousNode. (Compare and see what the differences are).

const newVnode = render(String(state.count))
patch(previousVnode, newVnode)
previousVnode = newVnode
Enter fullscreen mode Exit fullscreen mode

Create the click event

Our reactive DOM is now done. It will react to changes to the state.count variable. The last thing left for our counter to work is to increment the counter on the click of the #app element:

We just add onclick="state.count++" to it:

<div id="app" onclick="state.count++">
Enter fullscreen mode Exit fullscreen mode

And voilà! We have a simple click counter, we built on top of our own reactive library.

Congratulations! 🎉

Add some style

This of course still looks ugly. To make it a little bit fancier, add a <style>-tag to the top of the document and add the following CSS code to it:

* {
    user-select: none;
}
body {
    margin: 0;
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
#app {
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    background: #41b883;
    color: #ffffff;
}

h1 {
    font-size: 10rem;
    font-weight: 900;
    margin: 0;
}

p {
    margin: 0;
    text-align: center;
    font-weight: 100;
    font-size: 3rem;
}
Enter fullscreen mode Exit fullscreen mode

Summary

This is a very simple example, and could be done with way less code in simple JavaScript. However, keep in mind that this series is for educational purposes, so you get an insight to the internals of a frontend framework. It's not meant for you to create your own framework and use it in production. (Of course, you could.)

In this last part (for now), there we learned how to put together our own reactivity with our own virtual DOM to create a rudimentary reactive frontend framework. If you are keen to build more stuff on top of this, go ahead. You'll learn a lot in the process. If you built something, let me know and I'll check it out. Curious if someone uses this to learn stuff.

There might be a bonus coming some day where I build something more advanced with this "framework".

The code of this last part can be found on Github as well.

Original cover photo by Joshua Earle on Unplash, edited by Marc Backes.

Top comments (0)