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 🚘
- Introduction (this post)
- Virtual DOM basics
- Implementing the virtual DOM & rendering
- Building reactivity
- 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:
We would need a structure like this:
<div id="app">
<div id="container">
<h1>NUMBER_OF_CLICKS</h1>
<p>clicks</p>
</div>
</div>
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'),
)
}
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,
})
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:
- It's the first time our callback function gets called: We need to mount the template to the DOM
- 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)
}
})
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'))
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
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++">
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;
}
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)