1. Introduction to Virtual DOM
Today we are going to build a virtual DOM. Virtual DOM is a programming interface for HTML and XML documents. It represents a document as a tree, with nodes and children. It can be accessed and manipulated via JavaScript. We will build on the DOM of the official Vue.js website.
All right. So my talk might cut a little bit into the Q&A session, so that's why I'm just going right ahead with the presentation.
So today we are going to build a virtual DOM, but first we are going to look a little bit what it is.
So a little bit about myself first. My name is Mark, and this is where you can find me on Twitter. That's where I'm most active. So if after this you still have questions that I can't answer here or you just want to talk in general, just hit me up there. I'm DevRel Lead at WeAreDevelopers, where I help organize conferences such as our World Congress in July, which is super cool. I'm co-organizer of the Bellevue Chapter. It's the Belgian chapter of Vue.js. And I'm most recently a student pilot, which takes up all of my time which I don't spend working. So if there's any aviation geeks here, hit me up later, we can talk for hours about this stuff.
All right, so let's take a look at Virtual DOM. Well, what is it? Virtual DOM is a document object model. But what does it mean? So it's a programming interface for HTML and XML documents. And this represents a document as a tree. So you have a node and then you have one or more children. And then this could have one or more children. And until the end, there's nothing more to do than just render out whatever it is. It is to render out an image, text, whatever. And it can be accessed and manipulated via JavaScript. And this is how a DOM looks like. The DOM we all know and love. This is actually the DOM of view3.org. No, view3.org I think. Vue.js.org. Yeah, sorry. Yeah, official Vue.js website. You can see here some containers, like the app and the VP app, which then have children and so on and so on. So this is what we are going to build on.
2. Understanding the Virtual DOM
Virtual DOM is a virtual representation of the actual DOM. It decouples the rendering logic from the actual DOM, making it easier to manipulate and inspect. It can be used outside of the browser, such as in native mobile apps or WebGL renderers. In the virtual DOM, tags are represented as nodes, attributes as properties, and content as children. The H function in JavaScript can be used to create nodes with various structures.
Okay, so now we have a little bit of a picture of what is a DOM? Let's see what is a virtual DOM. And it's actually what the name suggests. It's a virtual representation of the actual DOM. So this could be, for example, JavaScript objects, which is exactly what we are going to do today. And this thing lives in the browser, or in the memory of the browser, and it is synchronized with the actual DOM. And this is what Vue.js uses under the hood, and also some other frameworks. But for example, Petite View doesn't use a virtual DOM, which is fine.
Now, you might ask yourself, OK, but some use it, some don't use it, but why should we use it, or why do we have a virtual DOM. Especially in the morning we heard Avenue say it has a lot of performance overhead, or some performance overhead. But let's take a look. So the great thing here is, it decouples the rendering logic from the actual DOM, and it's easier to programmatically manipulate and inspect the whole thing if we want to do something custom. It's also easier to reuse outside of the browser, for example, if you do a native mobile app, or, for example, a WebGL renderer. I heard Avenue once answer a question in an interview where he said that someone built a, based on the Vue 3 virtual DOM, a WebGL renderer, which is pretty cool.
All right, before we start building this thing, because we take a look at the actual DOM, what is it that we need to build here? So let's take a look at this very simple HTML markup, and what do we have here? So we have these things that we call tags, right? So we need to represent tags in our virtual DOM. So let's just write it down. We need tags. What else do we need here? These are attributes. It could also be a function, like an onClick event or something like that. So let's just call this properties, and then we have the last part, which is our content, but we're not going to call it content because it's a tree, so it's just the tree has children. We just call it children. But as you can see in this example, children isn't the most intuitive way of putting it because it could be just text, it could be just one item in there, or it could be various children. So in the last part it makes most sense to call it children, but this is how it's called.
Alright, so to create nodes we have this H function or Hyperscript, which means just JavaScript that produces HTML. So let's take a look at what we can create with this. So I represent here three different things we can do. Here you can see in the third parameter, or the third argument here, it's a text. So our virtual DOM, our DOM can have a text. What else can it have? It can have another virtual node which then has, in this case, another text. But it could be until, like, it can have as many children as necessary, or as you want. And then we have the third example, which is you have an array of these nodes.
3. Building a Virtual DOM
To build our own virtual DOM, we need a function to create virtual nodes, a way to mount them onto the DOM, and a patch function to update the DOM when needed.
So like lists, or just various items inside a container. Okay. So, what do we need in order to build our own virtual DOM? So, first we need a function to create virtual nodes. This is the H function that we saw here. We need something to mount this thing onto the DOM. This just means creating an actual DOM element and adding it to the actual DOM. And the reverse thing, just removing it, not just hiding it, but actually removing it from the DOM. And the last part is something called patch, when we want to update something. And this is where the magic of the virtual DOM comes in. Something changes, a value changes, and everywhere this is needed, it updates by patching the DOM by just applying these changes, so to say.
4. Creating the Virtual DOM
It's live coding time. I will show you my setup. We have an HTML file with boilerplate code. We import Tailwind for styling. We have an app container where we'll mount our DOM. We import the hFunction and mount function. We will create the VDOM from scratch. We have examples to guide us. Let's start by creating our virtual DOM.
All right, so it's live coding time. And yes. That guy gets it. So, can everyone read the text? Is it big enough? Yes. Okay, great. I can actually… Was it this? Yes. A little bit bigger. Okay.
So, I will show you very swiftly my setup here. It's nothing… Let's just close this. Oops. So, I have an irregular HTML file with a little bit boilerplate here. I import Tailwind just because it's easier to style things here. And then we have some… Just to center the thing, to make it a little bit more appealing. And then we have this app, this diff or this container with the app ID. So, this is where we are going to mount our application or our DOM. And then we import the hFunction, the mount function here, from our view VDOM. We will see what's in there in a second. And then we just look for our app element here, and then we mount whatever is the VDOM and onto that app. So, the VDOM isn't created yet. And I have some examples. But let's first view VDOM. What is to import there? Well, nothing yet, because we are going to create all of this from scratch in a second.
What else do we have in this project? So, we have examples. And this, we are going to use to step-by-step see what we need to add to our virtual DOM to, at the end, end up with, yes, a very simplified version of the Vue 3.0 virtual DOM. So, there is a lot of assumptions that I'm going to make here that just make the thing more simple. But the goal of this talk is that you just see what is a virtual DOM, how it looks like, and just to demystify a little bit this whole topic. So, we have a few different virtual DOMs here from easier to a little bit more complex. And we will see, we will go through each and every one of these one by one. Okay, so let's just start by creating our virtual DOM.
5. Implementing the Virtual DOM
And this is a new virtual node, an H1 tag with no properties and the content 'hello world'. We will implement the H function, mount function, unmount function, and patch function. The H function creates a virtual node with a tag, props, and children. The mount function mounts a virtual node onto the actual DOM. The unmount function removes a virtual node. The patch function compares and updates virtual nodes. The H function creates an object representing a virtual node. We will add a link to the actual DOM element later.
And this is a new virtual node and this is let's say an H1. We don't put any properties and we say hello world. And nothing will happen because we can already see there are errors.
But let's just start implementing our virtual DOM. So, we have a function that's called the H function. This is good. And we have the mount function that we mentioned before. We have the unmount and then we have the patch function. Okay.
So, we learned already the things that we need for this H function to create a virtual node. So, this is a tag, the props like attributes and stuff, functions, and the content which we named children. I will just keep it like this. Okay. And then mount. What are we going to do? Well, we want to mount one of these virtual nodes that the H tag creates onto the actual DOM, so we expect a virtual node. And I call this container where we want to mount it on to. So, in the simplest example is we saw before the container, the div with the id app, that's going to be in the first run our container in this case.
And when we want to unmount something, we just need the virtual node. We don't need a container or anything because we can deduct it from our virtual node. We will see in a second how this works. And patch is we want to compare one virtual node with another virtual node. And we apply changes to the actual DOM, comparing virtual node 1 to virtual node 2.
So the probably easiest thing to do here is to write this hfunction because our virtual node has a very flat hierarchy which is just like this. Return, the tag, props, and children. And then we already have it. That's all there is to this hfunction. It just creates an object that later we will add a few things there. Actually, we add one thing there, which is a link towards the element in the actual DOM, which is going to be called L, but we will add it later. Okay, so this is so far so good, but when we save it, nothing happens yet. Does not export named aged.
6. Mounting the Virtual DOM
We create an element using document.createElement and set its tag to vnode.tag. Then, we mount the element onto the container using appendChild. We add the text content to the element using element.textContent. Finally, we import examples from our files and apply properties to the element using element.setAttribute.
It does export. Don't I see something? Okay, now it works, whatever.
Okay, good. So now we have our virtual node here, and now we want to mount it. So the first thing is we want to create an element here, and we just use document.createElement, and this would be like h1, and here the text, right? But we know what this tag will be because we have it in our virtual node. Remember the virtual node here is the result of this h function, so we will have access to the tag here. So we will just write vnode.tag. And then the content will be vnode.... No, wrong. So we just create the element tag. And then we mount this thing onto the actual DOM, onto the container, so container.add, no, always wrong, append, child. Append, what are we going to append? We are going to append our element here. So let's see what happens. So nothing shows, but if we inspect this thing here, we open, da, da, da, da, da, da, and we can see it actually added the tag that we created. It just doesn't have a text or content yet because, well, we didn't write it, so let's just do that. Let's just say we have here our example, create H1, with, we just ignore for this first example the props. It will just have a text. Let's just assume for the moment, for the time being, that this is a text, so we go element.text, text.content is, and it's Vno.children. And there we go, we mounted our first virtual DOM, a very simple one, onto our browser. That's so far so good.
So now we have, I will remove this here, and we'll import a few, for every example I will import this from our examples files. So let's see the first example of what it is. So this is a test, and let me just open, no, okay, here, examples. So our first example is pretty similar to what we had before. It's an H1, it has a text, but it also has properties here. And this we want to apply to this element. So we want to add some class with blue text, bigger font, and so on. So what do we have to do here? So we have to add or set an attribute. So we just go element, set attribute, and it's going to be something like class, and then it's going to be the content here. So we have to iterate five minutes.
7. Implementing Event Handling
We iterate over the props and apply them to the vnode. We inspect the second example, a button, and find that the click function is not working. We determine that the key should start with 'on' for event handling. We remove the 'on' from the key and convert it to lowercase. Finally, we replace the string with a function.
Okay, we have to speed this up. So we have to iterate over the props, which is going to be for the key in vnode.props. Then we just apply this thing here. So the key, and then we have the vnode.props at the index of key. We can see here in the actual DOM this works and it also is applied here in the browser. Very cool.
So let's see our second example here. Virtual DeVDom2, which is a button. Let's take a look at it, the examples. So it's basically what we already have. It's rounded. It's grey. So this works. But it's now a click function and when you click it, we want to alert the rock sign. But when we click it, nothing is rocking. So this isn't working. So if we inspect this again, we see it's on click and then it's just a string here which does nothing. So why? Because we just told it to be a string. So let's just try, how could we do this? So let's just assume that when there's an event you want to have, it just starts with on, on input, on click or something. So let's just try, if the key starts with on, then we do a certain thing and if not, then we do this. All right, so we see already here, we remove the on click, because we entered here and it's not doing anything yet. But we need to do something like this, element.addEventListener, it's going to be something like click, and then the function.
What we have here is on click, and we want to have it like this. Is this contrast wise? Can you read? I can read. Okay, so we need to make some text transformation here. So we just say, event.name is the key. We want to erase the first two characters. So just slice two, and we convert it to lowercase. So this should be good enough. We just replace this, and what is going to be in there? Well, it's just going to be a function.
8. Mounting Virtual Nodes
We can reuse the same thing here and it works. Next, we have an object, which is just a virtual node. We need to render it again by mounting it recursively. If the vnode children is a string, we add it as a string. Otherwise, we assume it's an object and mount it onto the element.
So we can just use the same thing here. Save it. Oh, event listener. Okay, there we go. Thank you, and we click, and it works. So this is cool.
Next one, we have to sprint a little bit. So here we have an object. Why is it an object? Let's see what we want here. In the examples. Well, because it literally is an object. It's just a virtual node.
Okay, so what do we need to do here in this case? Well, we need to render it out again, so we need to mount it again. So this is going to be a recursive thing. So what we did here is... Let's just copy this down here. So we just assumed it's a text, but it's not always going to be a text. It can also be the type... Let's just see. If the type of vnode children is a string, then do Q&A time. I will finish this example, I show you the code of the next and then... 10 minutes. 10 minutes. Okay. Okay. We have some more time. So if it's, let's just say, we assume if it's string, just add string. If not, let's just assume it's an object. And then we mount the object. So what do you want to do? Mount the virtual node onto the element.
9. Rendering a List of Objects
We had 10 more. The next one is like a list of objects. We iterate over all the items and render them out individually. We're running out of time. The next one would have been fun. We have dogs, monkeys, and goats.
Does this make sense? No. You hold Q&A. Yeah, mount, VNode, children. Thank you.
Okay. So it works. It's now rendered out this way. So we had 10 more. Yeah, I will make one more example, and then I will be a little bit faster. Okay. So the next one is like a list of objects. So basically what we did here is just an array instead of an object, and we just need to render each item out. Let's just load this here. We can see it doesn't work, but if we just assume here else, if it's an array, let's just test for array. Array.isArray, V-node, children. Okay. Okay, then, oh, something already changed. This is good. Then we just iterate over all the items and we render them out individually. So, basically, the same that we did here, but for all of them because it's an array. So just V-node, children.forEach. What do we have there? A child, and then we mount this child onto the element. And then we can see again that it works. Okay.
We're running out of time. I will just show you how it would be. So we have the next one. The next one would have been fun. So we have dogs, monkeys, and goats. And we want to remove here.
Unmounting Monkeys and Patching Functions
We want to unmount the monkeys. We ask the parents of the actual DOM element to remove the child. Next, we discuss patching a function, the most complicated function in the virtual DOM. We check for differences in items, attributes, and content. Thank you for attending. Let's jump into our Q&A session.
We want to unmount the monkeys. And I think I have resolved—well, it's an extended thing here, but what we basically would do is we ask the parents of the actual DOM element to remove the child. And then it would remove this element that we say to remove. By the way, I will put up the GitHub repo. So you can all take a look at this at home.
So the next one would be already the last one. The next one would be how to patch a function. So I won't code it out now, because we don't have time, but basically what we are doing is this is the most complicated function that we have in our virtual DOM. So we check what is different. So is the number of items, is it shorter, is it longer? The number of attributes, we have to see did an attribute change, did the content just change of a certain item, and then there's a lot of if else, if else, if else, until we find out the most optimum way to re-render this virtual DOM.
So, sorry for going over time, but this is basically it. You saw the most important part, which is just create a virtual node, and then step by step add this, edit it, add it to the actual DOM. So with that, just, thank you. Here is the repo. Thanks a lot for this, Mark. Loved the session. My name is actually People Call Me Dom, because my name is Domagoj, and I'm a front-end engineer, so I'm probably meant to be a front-end engineer. Feel free, take a seat. Let's jump into our Q&A session. The first one is not a question, but somebody has called and wants to turn on the AC, but we probably want to turn it off. Let's turn off the AC, so that people don't get freezed. Right now I have one question, and it is, why should I declare the virtual DOM like this, instead of using regular Vue components? I don't really get the question, because when you have a regular Vue component, I suppose you talk about the template, and the template is compiled into a render function, which then is your virtual DOM. So I don't really understand. I think that they meant to ask that why would you use these approaches instead of just using the regular Vue approach. Oh, okay, I see. So this talk was more to show how it works behind the scenes. You wouldn't code this in your project. This is to show how the virtual DOM works behind the scenes, to demystify it. So you wouldn't use this anywhere. This is just to play with and to find out how it actually works.
Improvements and Changes to Virtual DOM
The virtual DOM is super elegant and works behind the scenes. The hierarchy of virtual DOM nodes in Vue 3 is flatter and easier to navigate.
Okay. Nice. Thanks. The next one is, well, you received an emoji probably for life coding. Nice. The next question is, what would you like to improve or change from how virtual DOM currently works? That's an excellent question, to which I don't have an answer. Like, I find it like, I mean, I looked into how it works. So I didn't invent it. So I just found out how it works. And for me, it's like super elegant. So I wouldn't know what I would actually change in it. Because you don't really work with it per se. Because it works behind the scenes like this. So yeah, well, you have your render function. One thing that I know that updated, or that changed from Vue 2 to Vue 3, is just that the hierarchy of these virtual DOM nodes is flatter and easier to navigate. So that's one improvement they did from Vue 2 to Vue 3. But what I would change now I have no idea.
Virtual DOM and Vue 3
Virtual DOM is a performance optimization that comes with an overhead. VaporModes may remove the virtual DOM and use direct DOM manipulation for better performance. The code presented is similar to how Vue's virtual DOM works, with some minor deviations. The virtual DOM for Vue 3 is available on GitHub for further investigation.
Yeah, it's really cool. I remembered when I first heard about virtual DOM, that was like so many years ago with React. And yeah, it was just like magic. You don't need to do any optimization. You have virtual DOM and everything works. Yeah. It just works, right? Yeah, it just works.
But we have another interesting question about VaporMode is going to remove virtual DOM. What do you think about that? I mean, the reasons that I said we have a virtual DOM, I don't know if maybe these use cases are not present anymore. Maybe it's not something that developers want to do anymore or that we want to promote anymore. So in this case, there wouldn't be a virtual DOM, so there would be direct DOM manipulation. So this would actually make it more performant. But yeah, I would actually have loved to see it. Because for me, it's like a way to add something on top of it, which then would be removed. Yeah, I'm not sure if they're having some new implementation because they don't know about VaporModes. But yeah, virtual DOM is performance optimization and they always come with an overhead. So probably they found a better way to do it. But, yeah, well, we need to investigate about VaporModes. I mean, when Evan talked in the morning about, oh yeah, the virtual DOM is problematic, and I was like, oh man, you're stepping on my talk. Okay, that's another well done for live coding. Thank you. Really, really brave.
And let's ask a final question. How close is this to how Vue's virtual DOM works? Well, it is, I think it is pretty, like, I mean, the code is more readable, because I made it for understanding it. But a lot of these parts actually work this way, like iterating over the props. How I did the event handler thing, I'm not 100% sure if that's how they do it. I think, yes. I think I saw that somewhere. And then the patch function that we didn't even touch, that would have been, like, the most deviation from how it actually works, because the function in reality is, like, this big, and mine would have been, like, five lines of code. Yeah, but correct me if I'm wrong, but, like, virtual DOM for Vue 3 is available on GitHub. Yeah. Yeah, yeah. So, yeah, feel free to investigate more and see how it works. But, yeah, this was everything. There are many other questions, so feel free to join Mark upstairs in the speaker's corner. And Mark, thanks a lot for these wonderful presentations. Make some noise for Mark.
Comments